7
System.out.println(
    new URI("http", "example.com", "/servlet", "a=x%20y", null));

结果是http://example.com/servlet?a=x%2520y,其中查询参数值与提供的值不同。奇怪,但这确实遵循 Javadoc:

“百分比字符 ('%') 总是被这些构造函数引用。”

我们可以传递解码后的字符串,a=x y然后我们得到一个合理的(?)结果a=x%20y

但是如果查询参数值包含一个“&”字符呢?例如,如果值是带有查询参数的 URL 本身,就会发生这种情况。看看这个(错误的)查询字符串: a=b&c。& 符号必须在此处转义 ( a=b%26c),否则可以将其视为查询参数a=b和一些垃圾 ( c)。如果我将它传递给 URI 构造函数,它会对其进行编码,并返回错误的 URL:...?a=b%2526c

这个问题似乎使 java.util.URI 无用。我在这里错过了什么吗?

答案摘要

java.net.URI 确实知道 URI 的查询部分的存在,但它不了解查询部分的内部结构,每个方案可能不同。例如 java.net.URI 不理解 HTTP 查询部分的内部结构。如果 java.net.URI 将查询视为一个不透明的字符串,并且没有改变它,这不会是一个问题。但它尝试应用一些通用的百分比编码算法,这会破坏 HTTP URL。

因此,我不能使用 URI 类从它的各个部分可靠地组装一个 URL,尽管它有构造函数。我还要提一下,从 Java 7 开始,相对化操作的实现非常有限,仅当一个 URL 是另一个 URL 的前缀时才有效。这两个功能(以及用于这些目的的更精简的界面)是我对 java.net.URI 感兴趣的原因,但它们都不适合我。

最后,我使用 java.net.URL 进行解析,并编写代码以从部分组装 URL 并将两个 URL 相对化。我还检查了 Apache HttpClient URIBuilder 类,虽然它确实了解 HTTP 查询字符串的内部结构,但从 4.3 开始,在处理整个查询部分时,它与 java.net.URI 之类的编码有同样的问题。

4

4 回答 4

1

查询字符串

a=b&c

在 URI 中没有错。URI Generic Syntax 上的RFC 状态

查询组件是要由资源解释的信息字符串。

  query         = *uric

在查询组件中,
保留字符“;”、“/”、“?”、“:”、“@”、“&”、“=”、“+”、“”和“$”。

查询字符串中的字符&非常有效(uric代表保留、标记和字母数字字符)。RFC 还指出

许多 URI 包括由某些特殊字符组成或由某些
特殊字符分隔的组件。这些字符被称为“保留”,因为
它们在 URI 组件中的使用仅限于它们的保留
用途。 如果 URI 组件的数据与保留的用途发生冲突,则必须在 形成 URI
之前对冲突数据进行转义。

因为它&是有效但保留的,所以由用户决定它是否要被编码。

您所说的查询参数不是 URI 的功能,因此URI该类没有理由(也不应该)支持它。

有关的:

于 2013-11-11T22:36:35.077 回答
1

我发现的唯一解决方法是使用单参数构造函数和方法。请注意,您必须使用URI#getRawQuery()以避免解码%26。例如:

URI uri = new URI("http://a/?b=c%26d&e");
// uri.getRawQuery() equals "b=c%26d&e"

uri = new URI(new URI(uri.getScheme(), uri.getAuthority(),
        uri.getPath(), null, null) + "?f=g%26h&i");
// uri.getRawQuery() equals "f=g%26h&i"

uri = uri.resolve("?j=k%26l&m");
// uri.getRawQuery() equals "j=k%26l&m"
// uri.toString() equals "http://a/?j=k%26l&m"
于 2016-01-27T21:41:21.083 回答
0

我知道的单一工作解决方案是反射(参见https://blog.stackhunter.com/2014/03/31/encode-special-characters-java-net-uri/

URI uri = new URI("http", null, "example.com", -1, "/accounts", null, null);
Field field = URI.class.getDeclaredField("query");
field.setAccessible(true);
field.set(uri, encodedQueryString);
//clear cached string representation
field = URI.class.getDeclaredField("string");
field.setAccessible(true);
field.set(uri, null);
于 2016-04-11T21:16:28.530 回答
-1

使用URLEncoder.encode()方法,在您的情况下,例如:

URLEncoder.encode("a=x%20y", "ISO-8859-1");
于 2013-11-11T22:21:05.300 回答