Encoding both path variables and request parameters correctly with UriComponentsBuilder

Viewed 665

I am struggling with getting UriComponentsBuilder#queryParam and UriComponents#expand to work together correctly while also correctly encoding all special characters in both path and query parts of the URI. I am particularly struggling with the '+' character. Either, it is double-encoded as %252B, or it is not encoded at all. I need to have it encoded correctly for sending ISO-8601 dates as request parameters.

When I do this:

@Test
public void test {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://www.google.com/{test}/");

Map<String, Object> expandParams = new HashMap<>();
expandParams.put("test", "junit 5");
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("q", "junit+5");

for (Map.Entry<String, Object> entry : queryParams.entrySet()) {
    builder = builder.queryParam(entry.getKey(), entry.getValue());
}

UriComponents uriComponents = builder.build();
if (MapUtils.isNotEmpty(expandParams)) {
    uriComponents = uriComponents.expand(expandParams);
}

final URI uri = uriComponents.encode().toUri();

assertEquals("https://www.google.com/junit%205/?q=junit%2B5", uri.toString());
}

the test fails:

Expected :https://www.google.com/junit%205/?q=junit%2B5  
Actual   :https://www.google.com/junit%205/?q=junit+5

Thus, the '+' sign is not encoded.

I know that I can do the encoding myself and tell the build() method that I did:

@Test
public void test {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("https://www.google.com/{test}/");

Map<String, Object> expandParams = new HashMap<>();
expandParams.put("test", "junit 5");
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("q", encodeValue("junit+5"));

for (Map.Entry<String, Object> entry : queryParams.entrySet()) {
    builder = builder.queryParam(entry.getKey(), entry.getValue());
}

UriComponents uriComponents = builder.build(true);
if (MapUtils.isNotEmpty(expandParams)) {
    uriComponents = uriComponents.expand(expandParams);
}

final URI uri = uriComponents.encode().toUri();

assertEquals("https://www.google.com/junit%205/?q=junit%2B5", uri.toString());
}

private static String encodeValue(String value) {
   try {
        return URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
    } catch (UnsupportedEncodingException e) {
        // should not happen, we know the encoding is supported
        return value;
    }
}

But this throws an exception because of the path variable. java.lang.IllegalArgumentException: Invalid character '{' for PATH in "/{test}/" And if I use build() or build(false) in this case, I get double encoding:

Expected :https://www.google.com/junit%205/?q=junit%2B5  
Actual   :https://www.google.com/junit%205/?q=junit%252B5

I tried to expand the path variables first and only then add the query parameters, but I couldn't get this to work either.

Any help is appreciated.

0 Answers
Related