如何在Java中对URL进行编码以避免特殊字符?

41

我需要Java代码来编码URL,以避免特殊字符,如空格、%和&等。


9
这种问题恰恰是为什么有些网站从来不在它们的 URL 中使用需要转义/编码的字符。像 StackOverflow 这样的网站似乎就是这样的一个例子(看看那干净利落的 URL 就知道了)。我认为,在 URL 中链接到包含空格等字符的文件(或者说在文件名中使用空格字符)的人应该被批判。而那些反对这个事实的人更应该受到谴责 ;) - SyntaxT3rr0r
请提供需要翻译的具体编程内容。 - halfer
5个回答

75

URL构建很棘手,因为URL的不同部分允许使用不同的字符:例如,加号在URL的查询组件中被保留,因为它代表一个空格;但在URL的路径组件中,加号没有特殊含义,而空格会被编码为 "%20"。

RFC 2396 在第2.4.2节中解释了一个完整的URL总是以其编码形式呈现:你需要获取各个组件(如协议、主机、路径等)的字符串,根据每个组件的规则进行编码,然后将它们组合成完整的URL字符串。试图构建完整的未编码URL字符串,然后单独对其进行编码会导致微妙的错误,例如路径中的空格被错误地更改为加号(符合RFC的服务器将其解释为真正的加号,而不是编码的空格)。

在Java中,构建URL的正确方式是使用URI类。使用其中接受URL组件作为单独字符串的多参数构造函数,它将根据组件自己的规则正确地对每个组件进行转义。 toASCIIString()方法将为您提供一个经过正确转义和编码的字符串,可以将其发送到服务器。要解码URL,请使用单个字符串构造函数构建URI对象,然后使用访问器方法(如getPath())检索解码的组件。

不要使用URLEncoder类!尽管名称是这样,但该类实际上执行的是HTML表单编码,而不是URL编码。将未编码的字符串连接在一起以创建“未编码”的URL,然后通过URLEncoder传递它不正确。这样做会导致问题(特别是路径中的空格和加号问题)。


我正在尝试自己编写代码...这样正确吗?public static String encode(String str) { StringBuilder sb=new StringBuilder(); for(int i=0;i<str.length();i++){ switch(str.charAt(i)){ case ' ': sb.append("%20");break; case '!': sb.append("%21");break; case '"': sb.append("%22");break; default: sb.append(str.charAt(i)); } } return sb.toString(); } - Adham
1
@adham,不,正确的URL编码远不止转义这三个字符。这段代码可能对某些URL产生正确的结果,但有许多URL它无法正确处理。只需使用URI类。 - Wyzard
是的,我知道还有很多其他字符...但是请你能给一个URI类的简单示例吗? - Adham
从测试结果来看,似乎您仍然需要手动将查询中的空格替换为“+”符号。我有哪里做错了吗? - Edward Falk
1
@EdwardFalk,你说得对,URI 在查询字符串中似乎不会将空格转换为 '+'。我曾经发誓过它会这样做,因为我在解决自己代码中的空格/加号问题时学习了 URI 类,但那是几年前的事情,我已经记不清细节了。由于加号实际上是 application/x-www-form-urlencoded MIME 类型的一部分,而不是 URI 语法本身,所以在将其放入 URI 之前,只需要通过 URLEncoder 运行(仅)查询字符串即可。虽然我需要重新考虑和可能重写我的答案。 - Wyzard
我认为你说得完全正确。 - Edward Falk

13

我也花了很多时间研究这个问题,这是我的解决方案:

String urlString2Decode = "http://www.test.com/äüö/path with blanks/";
String decodedURL = URLDecoder.decode(urlString2Decode, "UTF-8");
URL url = new URL(decodedURL);
URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
String decodedURLAsString = uri.toASCIIString();

Idea Intellij 添加了建议将 "UTF-8" 转换为 StandardCharsets.UTF_8 并在代码重新格式化时自动执行。然后它会破坏代码 - 出现错误 java: incompatible types: java.nio.charset.Charset cannot be converted to java.lang.String。我通过使用 StandardCharsets.UTF_8.name() 代替 "UTF-8" 来修复它。 - parsecer

8
如果您不想手动处理,可以使用Apache Commons - Codec库。您需要查看的类是:org.apache.commons.codec.net.URLCodec
String final url = "http://www.google.com?...."
String final urlSafe = org.apache.commons.codec.net.URLCodec.encode(url);

4
为什么这个回答被踩了? - Christian Vielma
5
请注意,URLCodec旨在替换URLEncoder / URLDecoder。它不是用来编码整个URL的,这就是示例所显示的内容。这意味着甚至http://也会变成http%3A%2F%2F。除非您确实想这样做。 - Nicholi
1
两者都给我相同的“错误”结果 URLCodec urlCodec = new URLCodec(); System.out.println(urlCodec.encode("FOO BAR")); // FOO+BAR System.out.println(URLEncoder.encode("FOO BAR", "UTF-8")); FOO+BAR - Topera

2

这是我相当简单的解决方案:

不要对url本身进行编码,而是对传递的参数进行编码,因为参数是用户输入的,用户可能会输入任何意外的特殊字符,所以这对我很有效 :)

String review="User input"; /*USER INPUT AS STRING THAT WILL BE PASSED AS PARAMTER TO URL*/
try {
    review = URLEncoder.encode(review,"utf-8");
    review = review.replace(" " , "+");
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}
String URL = "www.test.com/test.php"+"?user_review="+review;

1
抱歉,我不小心这样做了,但这个解决方案有效。 - Shahid Sarwar
我也在使用相同的方法,但它只在对字符串进行编码时生成%2c或其他字符。 - amit pandya

1

我想重申Wyzard所写的内容,但是还要补充一点:

  • 对于查询参数,HTML编码通常正是服务器所期望的;在这之外,使用URLEncoder是不正确的
  • 最新的URI规范是RFC 3986,因此您应该将其作为主要参考来源

我之前写过一篇关于这个主题的博客文章:Java:安全字符处理和URL构建


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