【问题标题】:java.net.URI and percent in query parameter value查询参数值中的 java.net.URI 和百分比
【发布时间】:2013-11-23 21:18:31
【问题描述】:
System.out.println(
    new URI("http", "example.com", "/servlet", "a=x%20y", null));

结果为@​​987654322@,其中查询参数值与提供的不同。奇怪,但这确实遵循 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 之类的编码有同样的问题。

【问题讨论】:

    标签: java encoding uri percent-encoding


    【解决方案1】:

    查询字符串

    a=b&c
    

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

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

      query         = *uric
    

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

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

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

    由于& 是有效但保留的,因此由用户决定是否要对其进行编码。

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

    相关:

    【讨论】:

    • 是的,a=b&c 在语法上是有效的,但它并不意味着明显的意图:一个名为 a 的查询参数,其值为 b&c。 & 符号必须被转义,但 URI 在 toString() 中返回一个混乱的 URL。让我们看一个更现实的例子,我们在return 参数中传递一个相对URL mypage?hello=world。完整、有效的 URL 是:http://example.com/some?return=mypage%3Fhello%3Dworld。我应该在 java.net.URI 多参数构造函数中传递什么来取回这个完整的 URL?
    • @HontváriJózsefLevente 查询参数与 HTTP 上下文相关。但 URI 不仅与 HTTP 上下文相关。查询参数由 HTTP 服务器解释。在 URI 中,它们没有任何意义,因此您无法使用 URI 类进行任何特殊格式化。
    • java.net.URI 不需要了解查询部分的内部结构。例如,如果它的多参数构造函数不改变我传递给它们的完全有效的查询字符串就足够了。
    • @HontváriJózsefLevente 您将哪个完全有效的查询字符串传递给它并且它改变了它? a=x%20y 不是有效的查询字符串。请注意,RFC 声明 Under normal circumstances, the only time when octets within a URI are percent-encoded is during the process of producing the URI from its component parts。所以a=x%20y 变成了a=x%2520y。 javadoc 指出,除了一些小的偏差java.net.URI 实例代表一个 URI 引用。
    • & 既可以是查询组件中的分隔符,也可以是数据字符。在后一种情况下,它必须是百分比编码的。因为 URI 不了解查询组件的内部结构,所以它无法确定与号是分隔符还是数据字符。因此,正如您所写,由用户(即我的代码)来决定哪个。现在,如果我对 & 符号进行百分比编码,它们不是分隔符而是数据字符,那么 URI.toString() 返回一个错误的字符串。我仍然不知道应该将什么传递给 URI 多参数构造函数来取回我上面写的示例 URL。
    【解决方案2】:

    我发现的唯一解决方法是使用单参数构造函数和方法。请注意,您必须使用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"
    

    【讨论】:

      【解决方案3】:

      我知道的单一工作解决方案是反射(请参阅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);
      

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

        • 我会使用一些 java.net.URI 特定的功能:带有查询和权限参数的构造函数以及相对化操作。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-09
        • 1970-01-01
        相关资源
        最近更新 更多