Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encoding query parameters with UriComponentsBuilder

I am struggling to understand the behavior of UriComponentsBuilder. I want to use it to encode a URL in a query parameter, however it appears to only escape % characters, but not other necessary characters such as &.

An example of a URL in a query parameter that is not encoded at all:

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

Output: http://example.com/endpoint?query=/path?foo=foo&bar=bar

This is not correct, because the unencoded & causes bar=bar to be interpreted as a query parameter to /endpoint instead of /path.

However, if I use an input that contains a % character::

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

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

The % character is escaped.

It seems inconsistent that UriComponentsBuilder would automatically escape the % characters but not the other reserved characters.

What is the correct process to encode a URL into a query parameter with UriComponentsBuilder?

like image 825
Adam Millerchip Avatar asked Nov 10 '17 07:11

Adam Millerchip


2 Answers

In your example the build UriComponents object is not encoded or normalised. To ensure that encoding is applied:

  1. Encode it yourself by calling encode() method (see also normalize() method):

    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. Use build(true) method if the parameters used for constructing the UriComponents are already encoded

    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"
    

Under the hood HierarchicalUriComponents.encode(String) method performs the actual encoding. After few internal calls it invokes HierarchicalUriComponents.encodeBytes(byte[], HierarchicalUriComponents.Type) where HierarchicalUriComponents.Type enum controls which characters are allowed in which part of the URL. This check is based on RFC 3986. In short, Spring has it's own encoding logic for every single part of the URL.

like image 93
Karol Dowbecki Avatar answered Oct 02 '22 13:10

Karol Dowbecki


  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.

If you want to see how to use the same with RestTemplate refer : RestTemplate.exchange() does not encode '+'?

like image 26
Raj Rajeshwar Singh Rathore Avatar answered Oct 02 '22 12:10

Raj Rajeshwar Singh Rathore