Christopher Smith opened SPR-14256 and commented

UriComponentsBuilder does not encode query parameters, expecting them to be encoded by the client already. The documentation does not indicate one way or another whether pre-encoding is necessary, and it would be helpful to note that it is.


Affects: 4.3 RC1

Comment From: spring-projects-issues

Rossen Stoyanchev commented

Query parameters it does encode, so I'm not quite sure what scenario you're looking at. The following works and is documented in the Javadoc:

UriComponents uriComponents = UriComponentsBuilder.fromUriString("/path?q={var}")
        .buildAndExpand("a=b")
        .encode();

assertEquals("q=a%3Db", uriComponents.getQuery());

Comment From: spring-projects-issues

Tristan Lins commented

Here are two issues I have with the UriComponentsBuilder:

The `&` will be encoded twice:

```java 
@Grab(group='org.springframework', module='spring-web', version='4.3.10.RELEASE')
import org.codehaus.groovy.runtime.InvokerHelper
import org.springframework.web.util.UriComponentsBuilder

URI source = new URI("http://example.com?foo=b%26r") // ?foo=b&r
URI target = UriComponentsBuilder.fromUri(source).build().toUri()

assert Objects.equals(source, target): "${source} != ${target}"

Ends with AssertionError: http://example.com?foo=b%26r != http://example.com?foo=b%2526r


The & will not be encoded either:

@Grab(group='org.springframework', module='spring-web', version='4.3.10.RELEASE')
import org.codehaus.groovy.runtime.InvokerHelper
import org.springframework.web.util.UriComponentsBuilder

URI source = new URI("http://example.com?foo=b%26r") // ?foo=b&r
URI target = UriComponentsBuilder.newInstance()
                 .scheme("http")
                 .host("example.com")
                 .queryParam("foo", "b&r")
                 .build().toUri()

assert Objects.equals(source, target): "${source} != ${target}"

Ends with AssertionError: http://example.com?foo=b%26r != http://example.com?foo=b&r

Comment From: spring-projects-issues

Rossen Stoyanchev commented

In the second example, you're missing the instruction to encode:

URI target = UriComponentsBuilder.newInstance()
        .scheme("http")
        .host("example.com")
        .queryParam("foo", "b&r")
        .build()
        .encode()
        .toUri();

The reason the first encodes is because the constructor of URI encodes "%". In that case the source URI is already encoded and you could specify that through a flag on the build method:

URI target = UriComponentsBuilder.fromUri(source).build(true).toUri();

That said arguably when fromUri(URI) is used, the "encoded" flag should be assumed. That's an improvement we can make but please open a separate ticket since it's much more specific, i.e. something like "Double encoding issue when using UriComponents#fromUri(URI)".

Comment From: spring-projects-issues

Rossen Stoyanchev commented

Resolving as "Works as designed" based on original description.

Comment From: spring-projects-issues

Tristan Lins commented

Thanks for explanation, I give it a try :-)

Comment From: theaspect

For those who will come here, example with multiple params

UriComponentsBuilder.fromHttpUrl("http://localhost/path")
    .queryParam("v1","{v1}")
    .queryParam("v2", "{v2}")
    .buildAndExpand("a=b", "10")
    .encode()

// http://localhost/path?v1=a%3Db&v2=10

Comment From: nitesr

UriComponentsBuilder doesn't encode + in the value .. here is the test which it fails

UriComponents components = UriComponentsBuilder.fromHttpUrl("http://localhost/path")
                .queryParam("p","{v}")
                .buildAndExpand("a+b=")
                .encode();
assertEquals("p=a%2Bb%3D", components.getQuery());

Comment From: bclozel

@nitesr please avoid commenting on an old issue and creating a new one, this is duplicating efforts. See #29838