使用UriComponentsBuilder编码查询参数

时间:2017-11-10 07:24:47

标签: java spring url-encoding query-parameters

我正在努力理解UriComponentsBuilder的行为。我想用它来对查询参数中的网址进行编码,但它似乎只会转义%个字符,而不会转义其他必要的字符,例如&

查询参数中未完全编码的URL示例:

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

输出:http://example.com/endpoint?query=/path?foo=foo&bar=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将网址编码为查询参数的正确流程是什么?

2 个答案:

答案 0 :(得分:4)

在您的示例中,构建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. 如果用于构造build(true)的参数已经编码,请使用UriComponents方法

    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 :(得分:3)

  1. 使用的语法不正确 使用UriComponentsBuilder.fromUri()参数代替String URI。如果您要将URL作为String传递,则将其用作:

      UriComponentsBuilder
      .fromUriString("http://example.com/endpoint")
      .queryParam("query", URLEncoder.encode("/path?foo=%20bar","UTF-8"))
      .build();
    
  2. &是有效的URL字符,因此不会被编码,但是%并不是这个原因 被解码为%25

如果您想了解如何与RestTemplate一起使用,请参考:RestTemplate.exchange() does not encode '+'?