查询字符串参数的JavaURL编码

假设我有一个URL

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

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

随机单词500英镑银行$

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

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

实现这一点的最佳方法是什么?我尝试了URLEncoder和创建URI/URL对象,但没有一个是完全正确的。

1139685 次浏览

URLEncoder是要走的路。您只需要记住编码只有单个查询字符串参数名称和/或值,而不是整个URL,当然不是查询字符串参数分隔符&也不是参数名称-值分隔符=

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

当您仍然不在Java10或更新版本上时,请使用StandardCharsets.UTF_8.toString()作为字符集参数,或者当您仍然不在Java7或更新版本上时,请使用"UTF-8"


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

另请注意,有三个encode()方法。一个没有Charset作为第二个参数,另一个有String作为第二个参数,这会引发一个检查异常。不建议使用没有Charset参数的方法。永远不要使用它并始终指定Charset参数。javadoc甚至明确建议使用UTF-8编码,正如RFC3986W3C所要求的那样。

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

另见:

我不会使用URLEncoder。除了被错误地命名(URLEncoder与URL无关),效率低下(它使用StringBuffer而不是Builder,并且做了一些其他很慢的事情)它也太容易搞砸了。

相反,我会使用URIBuilderSpring的#1或Commons Apache#2。原因是您必须以不同于参数值的方式转义查询参数名称(即BalusC的答案q)。

上面唯一的缺点(我痛苦地发现)是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

您需要首先创建一个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以外的字符。这正是浏览器的做法。

在Android中,我将使用此代码:

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

其中Uriandroid.net.Uri

Apache HttpComponents库为构建和编码查询参数提供了一个简洁的选项。

使用HttpComponents 4. x:

URLEncodedUtils

对于HttpClient 3. x使用:

EncodingUtil

您可以在代码中使用以下方法将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;}
  1. 使用这个:

     URLEncoder.encode(query, StandardCharsets.UTF_8.displayName());

    或者这个:

     URLEncoder.encode(query, "UTF-8");
  2. 您可以使用以下代码。

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

该代码

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

在某些情况下,建议检查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平台测试提供的大约100个测试用例。

在我的情况下,我只需要传递整个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");}

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

使用SpringUriComponentsBuilder组件生成器

UriComponentsBuilder.fromUriString(url).build().encode().toUri()