在Java中编码URL查询参数

130

在Java中如何对查询参数进行编码以便于放到URL中?我知道这似乎是一个明显且已经被问过的问题。

有两个细节问题我不确定:

  1. 空格应该被编码为“+”还是“%20”?在chrome中,如果我输入“http://google.com/foo=?bar me”,chrome会将其编码为%20。
  2. 是否需要/正确将冒号“:”编码为%3B?Chrome没有这样做。

注意:

  • java.net.URLEncoder.encode似乎不起作用,它似乎用于对要提交的表单数据进行编码。例如,它将空格编码为+而不是%20,并编码了不必要的冒号。
  • java.net.URI不会对查询参数进行编码。

这个问题看起来很有用:https://dev59.com/Y3RB5IYBdhLWcg3w-8Lo - waterlooalex
2
查询部分的结构取决于服务器,尽管大多数服务器都期望使用“application/x-www-form-urlencoded”键/值对。请参见此处获取更多信息:http://illegalargumentexception.blogspot.com/2009/12/java-safe-character-handling-and-url.html - McDowell
9个回答

148

5
我提到了我认为它不是进行URL编码,而是对要通过表单提交的数据进行编码。你有什么评论? - waterlooalex
7
我最终使用了URLEncoder.encode方法,并将"+"替换为"%20"。 - waterlooalex
3
它将斜杠编码为“%2F”,难道不应该保留URL中的斜杠吗? - golimar
8
@golimar,不应该这样。你只需要给它参数值,而不是整个URL。考虑这个例子:http://example.com/?url=http://example.com/?q=c&sort=name。它应该对&sort=name进行编码吗?没有办法区分URL中的值。这正是你需要进行值编码的确切原因。请记住,值编码和URL编码是不同的。 - Pijusn
3
但实际上,斜杠是查询字符串参数值中的合法字符。 - Stijn de Witt
显示剩余8条评论

24

不幸的是,URLEncoder.encode() 无法生成有效的百分数编码(如RFC 3986规定的那样)。

URLEncoder.encode() 可以很好地编码所有内容,除了空格被编码为“+”。我能找到的所有Java URI编码器都只公开了编码查询、片段、路径等部分的公共方法,但没有暴露“原始”编码。这是不幸的,因为片段和查询可以将空格编码为 +,所以我们不希望使用它们。路径被正确地编码,但首先被“规范化”,因此我们也不能将其用于“通用”编码。

我能想到的最佳解决方案:

return URLEncoder.encode(raw, "UTF-8").replaceAll("\\+", "%20");

如果replaceAll()对你来说太慢了,那么我猜替代方法就是自己编写编码器...
编辑:我先前在这里放置的代码无法正确地对“?”、“&”和“=”进行编码。
//don't use - doesn't properly encode "?", "&", "="
new URI(null, null, null, raw, null).toString().substring(1);

1
“+”是一个完全有效的空格编码。 - Lawrence Dol
1
@LawrenceDol 这是真的,但有时 + 可能会被错误地解释 - 看看 C# https://blogs.msdn.microsoft.com/yangxind/2006/11/08/dont-use-net-system-uri-unescapedatastring-in-url-decoding/ - Ilya Serbis
我将不同的替代方案与 Javascript 的 encodeURIComponent 方法输出进行了比较,这是我尝试过的唯一完全匹配的方案(包括带有空格、土耳其和德国特殊字符的查询)。 - Utku Özdemir
Ahmet+Mehmet Demir => Ahmet%2BMehmet+Demir,根据我的理解,这里唯一的问题是MIME类型为application/x-www-form-urlencoded。在这种情况下,空格被编码为+字符,如果意图是通过GET请求在Web表单中搜索两个条目,例如Google搜索。 URI RFC允许+字符作为有效字符。因此,通常不需要对其进行转义。 - Davut Gürbüz

16

编辑:在更近的版本中,URIUtil不再可用,在Java - encode URL或Sindi先生的帖子中有更好的答案。

Apache httpclientURIUtil非常有用,虽然还有一些替代方案

URIUtil.encodeQuery(url);

例如,它将空格编码为 "+" 而不是 "%20"

在正确的情境下,两者都是完全有效的。虽然如果您真的喜欢,您可以进行字符串替换。


我必须同意。使用HttpClient,你会更加愉快。 - DaShaun
看起来很有前途,你碰巧有链接吗?我在谷歌上搜了很多但是没找到。 - waterlooalex
1
这个方法在HttpClient 4.1中似乎不存在?http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/utils/URIUtils.html - waterlooalex
你说得没错,这真的很烦人。目前有一个URLEncodedUtils.encodeFormFields方法,它是一个私有静态方法。将这个方法声明为公共方法岂不是更合理? - Cacovsky
1
URIUtil.encodeWithinQuery 是你用来编码单个查询参数的方法,这也是原问题所询问的。 - Jesse Glick
显示剩余2条评论

10

在查询中,不必将冒号编码为%3B,尽管这样做并不违法。

URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
query       = *( pchar / "/" / "?" )
pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
pct-encoded   = "%" HEXDIG HEXDIG
sub-delims    = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="

看起来只有百分号编码的空格是有效的,因为空格不是 ALPHA 或 DIGIT。

查看URI规范以获取更多详情。


1
但这样做可能会改变URI的含义,因为查询字符串的解释取决于服务器。如果您正在生成application/x-www-form-urlencoded查询字符串,则两者都可以。如果您正在修复用户键入/粘贴的URL,则应保留“:”。 - tc.
@tc。您是正确的,如果冒号被用作一般分隔符(RFC第12页); 但是,如果它不被用作一般分隔符,则两种编码应该解析为相同。 - Edwin Buck
你还必须小心,因为URL并不是URI的真正子集:http://adamgent.com/post/25161273526/urls-are-not-a-subset-of-uris - Adam Gent
冒号是%3A而不是%3B(那是分号),对于任何手动编码的人来说。 - Marcelino Lucero III

4

内置的Java URLEncoder正在执行其应该执行的操作,您应该使用它。

在URL中,“+”或“%20”都是空格字符的有效替换。任何一种方式都可以使用。

“:”应该被编码,因为它是分隔符。即http://fooftp://bar。某些浏览器可以处理未编码的情况,但这并不正确。您应该对它们进行编码。

作为良好实践的一部分,请确保使用带有字符编码参数的方法。通常使用UTF-8,但您应明确提供它。

URLEncoder.encode(yourUrl, "UTF-8");

6
+只是在application/x-www-form-urlencoded中表示空格的一种方式,即使限制在HTTP中,也不能保证其有效。同样,在查询字符串中,:是有效的且不应转换为%3B;服务器可以选择不同的解释方式。 - tc.
1
该方法还会对整个URL中的斜杠和其他字符进行编码,例如将http://编码为http%3A%2F%2F,这是不正确的。 - To Kra
3
@ToKra,你不应该对“http://”部分进行编码。该方法适用于查询参数和编码的表单数据。但是,如果你想将另一个网站的URL作为查询参数传递,那么你需要对其进行编码,以避免混淆URL解析器。 - beldaz
@tc 我对 https://www.w3.org/TR/html4/interact/forms.html#h-17.13.3.3 的理解是,所有的 GET 表单数据都会被编码为 application/x-www-form-urlencoded 内容类型。这难道不意味着它必须适用于 HTTP 吗? - beldaz

3
我只是想添加另一种解决此问题的方法。
如果您的项目依赖于Spring Web,则可以使用它们的实用程序。
import org.springframework.web.util.UriUtils

import java.nio.charset.StandardCharsets

UriUtils.encode('vip:104534049:5', StandardCharsets.UTF_8)

输出:

vip%3A104534049%3A5


0
String param="2019-07-18 19:29:37";
param="%27"+param.trim().replace(" ", "%20")+"%27";

我发现在Datetime(时间戳)的情况下,URLEncoder.encode(param,"UTF-8")不起作用。


0
当使用URLEncoder.encode时,空格字符“ ”会被转换为+符号。这与其他编程语言(如JavaScript)将空格字符编码为%20相反。但这是完全有效的,因为查询字符串参数中的空格由+表示,而不是%20。%20通常用于表示URI本身中的空格(即在?之前的URL部分)。

-3
如果您在URL中仅有空格的问题,我使用以下代码进行解决并且它可以很好地工作。
String url;
URL myUrl = new URL(url.replace(" ","%20"));

示例:网址为

www.xyz.com?para=hello sir

那么 muUrl 的输出结果为

www.xyz.com?para=hello%20sir


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接