当前位置: 首页 > 科技观察

Java创建URL常见问题及解决方法

时间:2023-03-13 06:00:30 科技观察

URL无处不在,但似乎开发者并没有真正理解它们,因为我经常在StackOverflow上看到有人问如何正确创建URL。如果你想知道URL语法是如何工作的,你可以阅读Lunatech的这篇文章,这篇文章非常好。本文不会深入介绍URL的所有语法(如果你想完全理解URL,可以阅读RFC3986、RFC1738和上面提到的文章,以及W3上的文档),我想说的关于这里是一些库中URL操作的常见错误,以及如何使用URL-builder正确使用它,这是我们发布的用于正确创建URL的Java库。问题一:Java的URLEncoder类不仅名字不好,而且其文档的第一句话也不太对。用于HTML表单编码的实用程序类。你可能会奇怪为什么叫URLEncoder,但是看到这一行你就彻底无语了。如果您阅读了Lunatech的那篇博文,您现在应该明白您无法通过此类奇迹般地将URL字符串转换为安全、正确编码的URL对象。当然,如果你没有做过功课,这里有个小例子可以帮助你理解。假设你有一个HTTP服务端点http://foo.com/search,它接受一个查询参数p,p的值就是要搜索的字符串。如果您搜索字符串“You&I”,您创建的第一个搜索的URL可能如下所示:http://foo.com/search?q=You&I。这当然行不通,因为&是分隔查询参数名称/值对的分隔符。如果你得到这个乱七八糟的URL字符串,你将无能为力,因为你一开始就无法正确解析它。好吧,让我们使用URLEncoder。URLEncoder.encode("You&I","UTF-8")就是结果是You+%26+I。这个%26是解码后的&,查询字符串中的+号代表一个空格,所以这个URL可以正常工作。现在假设您要使用查询字符串连接URL路径而不是将它们放在URL参数中。显然,http://foo.com/search/You&I是错误的。不幸的是,URLEncoder.encode()的结果也是错误的。http://foo.com/search/You+%26+I解码后会得到/search/You++&+I,因为+号在url路径中不会被解析成空格。URLEncoder或许可以满足你的一些场景。不幸的是,它过于通用的名称使开发人员很容易滥用它。所以最好的办法是不要用它,以免其他开发人员在你的基础上使用其他功能时出错(除非,你真的是在做“HTML表单编码”)。问题2:GroovyHttpBuilder和Java的URIHTTPBuilder是Groovy的HTTP客户端库。创建一个普通的GET请求非常简单:newHTTPBuilder("http://localhost:18080").request(Method.GET){uri.path="/foo"}此代码将发送GET/fooHTTP/1.1到服务器(您可以运行nc-l-p18080然后执行此代码来验证)。让我们尝试一个包含空格的URL。newHTTPBuilder("http://localhost:18080").request(Method.GET){uri.path="/foobar"}这发送GET/foo%20barHTTP/1.1,看起来不错。现在假设我们有一段路径称为foo/bar。这不能通过简单地发送foo/bar来完成,因为这将被视为路径中的两个段,foo和bar,所以让我们尝试foo%2Fbar(将/替换为相应的编码)。newHTTPBuilder('http://localhost:18080').request(Method.GET){uri.path='/foo%2Fbar'}这会发送GET/foo%252FbarHTTP/1.1。不是很好。%2F中的%被重复编码,所以解码后得到的路径是foo%2Fbar而不是foo/bar。其实这里真正的罪魁祸首是java.net.URI,因为这个HTTPBuilder中的URIBuilder类使用了它。上面代码中配置闭包暴露的uri属性的类型是URIBuilder。如果通过uri.path=...更新uri的path属性,最终会调用URI的一个构造函数,传入的path属性描述如下:如果提供了path参数,则追加到URL后面.路径中的字符,只要不是非保留、标点、转义等分类(译注:这些分类在RFC2396中有详细说明),并且不是/或@,都会被编码。这没有多大意义,因为如果未编码的文本包含特殊字符,它不会生成正确编码的路径段。也就是说,“我把这个字符串编码一下,编码后就正确了”,当然是一个谬论,而URI就是这个谬论的牺牲品。如果字符串编码正确,那很好,如果不正确,那就搞砸了,因为无法解析字符串。实际上,文档中所说的不转义/符号的意思是假定路径字符串已经被正确编码(即正确使用/分隔路径),而没有被正确编码(除了/其他部分仍然需要编码)。如果HTTPBuilder不使用URI类的这个有缺陷的特性就好了,当然如果URI本身没问题就更好了。正确的做法是写这个url-builder,可以帮助开发者方便的拼接各种类型的url。它遵循前几篇参考文献中的编码标准,并且还提供了一个流式API。下面的使用示例几乎可以涵盖所有的使用场景:UrlBuilder.forHost("http","foo.com").pathSegment("withspaces").pathSegments("path","with","varArgs")。pathSegment("&=?/").queryParam("fancy+name","fancy?=value").matrixParam("matrix","param?").fragment("#?=").toUrlString()结果是:http://foo.com/with%20spaces/path/with/varArgs/&=%3F%2F;matrix=param%3F?fancy%20%2B%20name=fancy?%3Dvalue#%23?=本例演示了URL各部分的不同编码规则。例如路径中允许未编码的&=,而?/需要编码,但是=需要在查询参数中编码,但是?不需要符号,因为它已经是查询字符串的一部分(译注:查询字符串以?