使用UriComponentsBuilder编码查询参数

10
我很难理解UriComponentsBuilder的行为。我想使用它来在查询参数中编码URL,但是它似乎只转义%字符,而不是其他必要的字符,例如&
以下是一个未编码的查询参数中的URL示例:
UriComponentsBuilder.fromUri("http://example.com/endpoint")
                    .queryParam("query", "/path?foo=foo&bar=bar")
                    .build();

输出结果: http://example.com/endpoint?query=/path%3Ffoo=foo%26bar=bar

这个不正确,因为未编码的&导致bar=bar被解释为/endpoint的查询参数,而不是/path的查询参数。

但是,如果我使用一个包含%字符的输入:

UriComponentsBuilder.fromUri("http://example.com/endpoint")
                    .queryParam("query", "/path?foo=%20bar")
                    .build();

输出:http://example.com/endpoint?query=/path?foo=%2520bar

字符%已经被转义。

似乎不太一致,UriComponentsBuilder自动转义了%字符,但是没有转义其他的保留字符。

使用UriComponentsBuilder将URL编码为查询参数的正确过程是什么?


1
但是,据我所知,&是URL中的有效字符。除了某些特定字符外,其他所有内容都必须进行URL编码。 - amdg
你找到解决方案了吗?我也遇到了类似的问题。 - IgorGanapolsky
2个回答

14
在您的示例中,构建的UriComponents对象未进行编码或标准化。为确保应用了编码,请执行以下操作:
  1. 通过调用encode()方法(请参见normalize()方法)对其进行自我编码:

    UriComponents u = UriComponentsBuilder.fromHttpUrl("http://example.com/endpoint")
      .queryParam("query", "/path?foo=foo&bar=bar")
      .build()
      .encode(); 
    // http://example.com/endpoint?query=/path?foo%3Dfoo%26bar%3Dbar
    
  2. 如果用于构建 UriComponents 的参数已经编码,请使用 build(true) 方法

  3. UriComponents u = UriComponentsBuilder.fromHttpUrl("http://example.com/endpoint")
      .queryParam("query", "/path?foo=foo&bar=bar")
      .build(true);
    // IllegalArgumentException: Invalid character '=' for QUERY_PARAM in "/path?foo=foo&bar=bar"
    

在幕后,HierarchicalUriComponents.encode(String) 方法执行实际的编码。在几个内部调用之后,它调用 HierarchicalUriComponents.encodeBytes(byte[], HierarchicalUriComponents.Type),其中 HierarchicalUriComponents.Type 枚举控制 URL 的哪个部分允许使用哪些字符。此检查基于RFC 3986。简而言之,Spring 为 URL 的每个单独部分都有自己的编码逻辑。


1
我不理解这个答案的后半部分。为了修复它,我需要做什么? - Raja Anbazhagan
太好了。我不知道build()可以带一个参数来避免编码。 - Raja Anbazhagan
你所提到的 normalize() 方法是什么?它在 UriComponentsBuilder 上不存在。虽然 UriComponents 上有一个方法,但与编码无关。 - Adam Millerchip

2
  1. The syntax is incorrect as you are using UriComponentsBuilder.fromUri() with String parameter instead of an URI. If you want to pass the URL as a String use it as :

      UriComponentsBuilder
      .fromUriString("http://example.com/endpoint")
      .queryParam("query", URLEncoder.encode("/path?foo=%20bar","UTF-8"))
      .build();
    
  2. & is a valid URL character so it will not be encoded but % is not that's why it gets decoded to %25.

如果您想了解如何使用相同的方法与RestTemplate一起使用,请参阅:RestTemplate.exchange() does not encode '+'?

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