查询字符串参数的Java URL编码


710

说我有一个网址

http://example.com/query?q=

并且我有一个用户输入的查询,例如:

随机词£500银行$

我希望结果是正确编码的URL:

http://example.com/query?q=random%20word%20%A3500%20bank%20%24

实现此目标的最佳方法是什么?我尝试URLEncoder创建URI / URL对象,但是没有一个是正确的。


24
“什么都不出来很正确”是什么意思?
马克·艾略特

2
我已经使用URI.create并在查询字符串中用+替换了空格。在客户端站点,当我选择查询字符串时,它会将+转换回空格。那对我有用。
ND27


您为什么期望$被百分比编码?
jschnasse

Answers:


1150

URLEncoder是要走的路。您只需要记住仅对单个查询字符串参数名称和/或值进行编码,而不对整个URL 进行编码,请确保对查询字符串参数分隔符&和参数名称-值分隔符都不进行编码=

String q = "random word £500 bank $";
String url = "https://example.com?q=" + URLEncoder.encode(q, StandardCharsets.UTF_8);

请注意,查询参数中的空格由+而不是表示%20,这是合法有效的。在%20通常被用于表示URI本身(URI查询字符串分隔符之前的部分的空间?),而不是在查询字符串(之后的部分?)。

另请注意,有三种encode()方法。一个不Charset带有第二个自变量,另一个不带有String第二个自变量,这将引发已检查的异常。不带Charset参数的不推荐使用。永远不要使用它,并且总是指定Charset参数。在javadoc中甚至明确建议使用UTF-8编码,通过的授权RFC3986W3C

所有其他字符都是不安全的,并且首先使用某种编码方案转换为一个或多个字节。然后,每个字节由3个字符的字符串“%xy”表示,其中xy是该字节的两位十六进制表示形式。推荐使用的编码方案是UTF-8。但是,出于兼容性原因,如果未指定编码,则使用平台的默认编码。

也可以看看:


URL中可以有两种类型的参数。查询字符串(后跟?)和路径参数(通常是URL本身的一部分)。那么,路径参数呢?URLEncoder甚至为路径参数也为空间产生+。实际上,它只处理查询字符串以外的任何内容。另外,此行为与节点js服务器不同步。因此,对我来说,这堂课很浪费,只能用于非常特殊/特殊的情况。
sharadendu sinha

2
@sharadendusinha:如记录和回答的那样,URLEncoder用于URL编码的查询参数符合application/x-www-form-urlencoded规则。路径参数不属于此类别。您需要一个URI编码器。
BalusC

正如我预料的那样...用户感到困惑,因为显然问题是人们需要编码的不仅仅是参数值。这是非常罕见的情况,您只需要对参数值进行编码。这就是为什么我提供“困惑的” Wiki答案来帮助@sharadendusinha之类的人的原因。
亚当·根特

1
@WijaySharma:因为特定于URL的字符也会被编码。仅当您要将整个URL作为另一个URL的查询参数传递时,才应该这样做。
BalusC

1
我需要听到“ +,而不是%20”。非常感谢。
wetjosh

173

我不会用URLEncoder。除了被错误地命名(URLEncoder与URL无关)之外,效率低下(它使用a StringBuffer代替Builder并执行其他一些很慢的操作)它也很容易弄乱它。

相反,我将使用URIBuilderSpring org.springframework.web.util.UriUtils.encodeQuery或Commons ApacheHttpClient。原因是您必须以q不同于参数值的方式转义查询参数名称(即BalusC的answer )。

上面的唯一缺点(我很痛苦地发现)是URL并不是URI的真正子集

样例代码:

import org.apache.http.client.utils.URIBuilder;

URIBuilder ub = new URIBuilder("http://example.com/query");
ub.addParameter("q", "random word £500 bank \$");
String url = ub.toString();

// Result: http://example.com/query?q=random+word+%C2%A3500+bank+%24

由于我只是链接到其他答案,因此将其标记为社区Wiki。随时编辑。


2
为什么与网址无关?
路易斯

15
@Luis:URLEncoder正如其javadoc所说的,旨在对查询字符串参数进行编码符合application/x-www-form-urlencodedHTML规范w3.org/TR/html4/interact/…中所述。确实有些用户确实混淆/滥用了它来对整个URI进行编码,就像当前的应答程序显然那样。
BalusC 2015年

8
简写为URLEncoder的@LuisSep用于表单提交的编码。这不是为了逃避。它与用于创建要放入网页中的URL的转义符不完全相同,但是恰好足够相似,以至于人们滥用它。唯一应该使用URLEncoder的情况是,如果您编写的是HTTP客户端(即使这样,仍有许多高级的编码选项)。
亚当·根特

1
@BalusC“ 有些用户确实确实混淆/滥用了它来对整个URI进行编码,就像当前的应答者显然这样做了。 ” 你以为错 我从未说过自己搞砸了。我刚刚看到其他做到这一点的人,这些是我必须修复的错误。我搞砸的部分是Java URL类将接受未转义的括号,但不接受URI类。有很多方法可以构造URL,但并不是每个人都像您一样聪明。我要说的是,大多数正在寻找URLEncoding的用户可能是“ 用户确实混淆/滥用 ”了URI转义。
亚当·根特

1
问题不是关于这个的,但您的答案暗示了这一点。
BalusC 2015年

99

您首先需要创建一个URI,例如:

String urlStr = "http://www.example.com/CEREC® Materials & Accessories/IPS Empress® CAD.pdf"
URL url= new URL(urlStr);
URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());

然后将该Uri转换为ASCII字符串:

urlStr=uri.toASCIIString();

现在,您的url字符串已完全编码,我们先进行了简单的url编码,然后将其转换为ASCII字符串,以确保字符串中没有剩余US-ASCII的字符。这正是浏览器的工作方式。


7
谢谢!您的解决方案有效,但是内置的URL.toURI()无效,这很愚蠢。
user11153 2015年

2
不幸的是,这似乎不适用于“ file:///”(例如:“ file:/// some / directory /一个包含spaces.html的文件”);它使用“ new URL()”中的MalformedURLException炸弹;任何想法如何解决这个问题?
ZioByte

您需要执行以下操作:String urlStr =“ some / directory /一个包含spaces.html 文件”; URL url =新URL(urlStr); URI uri =新URI(url.getProtocol(),url.getUserInfo(),url.getHost(),url.getPort(),url.getPath(),url.getQuery(),url.getRef()); urlStr = uri.toASCIIString(); urlStr.replace(“ http://”,“ file:///”); 我尚未对其进行测试,但我认为它可以工作。...:)
M Abdul Sami 2015年

1
@tibi,您可以简单地使用uri.toString()方法将其转换为字符串而不是Ascii字符串。
M Abdul Sami

1
我使用的API不接受+空格替代,但接受了%20,因此此解决方案比BalusC更好,谢谢!
朱利安·本玛

35

Guava 15现在添加了一组简单的URL逸出器


1
这些遭受了与相同的逃避规则URLEncoder
2rs2ts 2014年

3
不确定他们是否有问题。他们区分例如“ +”或“%20”来逃避“”(形式参数或路径参数),URLEncoder但不能。
Emmanuel Touzery 2015年

1
这对我来说是有效的,我只是将对URLEncoder()的调用替换为对UrlEscapers.urlFragmentEscaper()的调用,并且它工作了,但不清楚我是否应该使用UrlEscapers.urlPathSegmentEscaper()。
保罗·泰勒

2
实际上,它对我没有用,因为与URLEncoder不同,它不对'+'进行编码,而是单独处理,服务器将其解码为空格,而如果我使用URLEncoder,则+会转换为%2B并正确地解码为+
Paul Taylor

2
链接更新:UrlEscapers
mgaert


6

您可以在代码中使用此方法将url字符串和参数映射转换为包含查询参数的有效编码url字符串。

String addQueryStringToUrlString(String url, final Map<Object, Object> parameters) throws UnsupportedEncodingException {
    if (parameters == null) {
        return url;
    }

    for (Map.Entry<Object, Object> parameter : parameters.entrySet()) {

        final String encodedKey = URLEncoder.encode(parameter.getKey().toString(), "UTF-8");
        final String encodedValue = URLEncoder.encode(parameter.getValue().toString(), "UTF-8");

        if (!url.contains("?")) {
            url += "?" + encodedKey + "=" + encodedValue;
        } else {
            url += "&" + encodedKey + "=" + encodedValue;
        }
    }

    return url;
}

6
URL url= new URL("http://example.com/query?q=random word £500 bank $");
URI uri = new URI(url.getProtocol(), url.getUserInfo(), IDN.toASCII(url.getHost()), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
String correctEncodedURL=uri.toASCIIString(); 
System.out.println(correctEncodedURL);

版画

http://example.com/query?q=random%20word%20%C2%A3500%20bank%20$

这是怎么回事

1.将 URL分成结构部分。使用java.net.URL 它。

2. 正确编码每个结构部分!

3.使用IDN.toASCII(putDomainNameHere)的Punycode编码的主机名!

4.用于java.net.URI.toASCIIString()对NFC编码的unicode进行百分比编码(最好是NFKC!)。有关更多信息,请参见:如何正确编码此URL

在某些情况下,建议检查网址是否已编码。还要用“%20”编码的空格替换“ +”编码的空格。

这里有一些例子也可以正常工作

{
      "in" : "http://نامه‌ای.com/",
     "out" : "http://xn--mgba3gch31f.com/"
},{
     "in" : "http://www.example.com/‥/foo",
     "out" : "http://www.example.com/%E2%80%A5/foo"
},{
     "in" : "http://search.barnesandnoble.com/booksearch/first book.pdf", 
     "out" : "http://search.barnesandnoble.com/booksearch/first%20book.pdf"
}, {
     "in" : "http://example.com/query?q=random word £500 bank $", 
     "out" : "http://example.com/query?q=random%20word%20%C2%A3500%20bank%20$"
}

该解决方案通过了Web Plattform Tests提供的大约100个测试用例。


1

在android中,我将使用以下代码:

Uri myUI = Uri.parse ("http://example.com/query").buildUpon().appendQueryParameter("q","random word A3500 bank 24").build();

哪里Uriandroid.net.Uri


10
这没有使用标准的Java API。因此,请指定使用的库。
rmuller '16

1

以我为例,我只需要传递整个url并仅对每个参数的值进行编码。我没有找到执行此操作的通用代码(!!),所以我创建了这个小方法来完成这项工作:

public static String encodeUrl(String url) throws Exception {
    if (url == null || !url.contains("?")) {
        return url;
    }

    List<String> list = new ArrayList<>();
    String rootUrl = url.split("\\?")[0] + "?";
    String paramsUrl = url.replace(rootUrl, "");
    List<String> paramsUrlList = Arrays.asList(paramsUrl.split("&"));
    for (String param : paramsUrlList) {
        if (param.contains("=")) {
            String key = param.split("=")[0];
            String value = param.replace(key + "=", "");
            list.add(key + "=" +  URLEncoder.encode(value, "UTF-8"));
        }
        else {
            list.add(param);
        }
    }

    return rootUrl + StringUtils.join(list, "&");
}

public static String decodeUrl(String url) throws Exception {
    return URLDecoder.decode(url, "UTF-8");
}

它使用org.apache.commons.lang3.StringUtils


-2
  1. 使用此方法:URLEncoder.encode(query,StandardCharsets.UTF_8.displayName()); 或this:URLEncoder.encode(query,“ UTF-8”);
  2. 您可以使用以下代码。

    String encodedUrl1 = UriUtils.encodeQuery(query, "UTF-8");//not change 
    String encodedUrl2 = URLEncoder.encode(query, "UTF-8");//changed
    String encodedUrl3 = URLEncoder.encode(query, StandardCharsets.UTF_8.displayName());//changed
    
    System.out.println("url1 " + encodedUrl1 + "\n" + "url2=" + encodedUrl2 + "\n" + "url3=" + encodedUrl3);

4
不正确。您必须分别编码参数名称和值。对整个查询字符串进行编码还会对=&分隔符进行编码,这是不正确的。
user207421 '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.