java.net.URI和查询参数值中的百分比

时间:2013-11-11 22:17:05

标签: java encoding uri percent-encoding

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 个答案:

答案 0 :(得分:1)

查询字符串

a=b&c

在URI中没有错。 RFC on URI Generic Syntax states

  

查询组件是一串要解释的信息   资源。

  query         = *uric
     

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

查询字符串中的字符&非常有效(uric表示保留字符,标记字符和字母数字字符)。 RFC还声明

  

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

由于&有效但保留,因此用户需要确定是否要进行编码。

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

相关:

答案 1 :(得分: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"

答案 2 :(得分: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);

答案 3 :(得分:-1)

使用URLEncoder.encode()方法,例如:

URLEncoder.encode("a=x%20y", "ISO-8859-1");