When calling RestTemplate.exchange
to do a get request, such as:
String foo = "fo+o";
String bar = "ba r";
restTemplate.exchange("http://example.com/?foo={foo}&bar={bar}", HttpMethod.GET, null, foo, bar)
what's the proper to have the URL variables correctly escaped for the get request?
Specifically, how do I get pluses (+
) correctly escaped because Spring is interpreting as spaces, so, I need to encode them.
I tried using UriComponentsBuilder
like this:
String foo = "fo+o";
String bar = "ba r";
UriComponentsBuilder ucb = UriComponentsBuilder.fromUriString("http://example.com/?foo={foo}&bar={bar}");
System.out.println(ucb.build().expand(foo, bar).toUri());
System.out.println(ucb.build().expand(foo, bar).toString());
System.out.println(ucb.build().expand(foo, bar).toUriString());
System.out.println(ucb.build().expand(foo, bar).encode().toUri());
System.out.println(ucb.build().expand(foo, bar).encode().toString());
System.out.println(ucb.build().expand(foo, bar).encode().toUriString());
System.out.println(ucb.buildAndExpand(foo, bar).toUri());
System.out.println(ucb.buildAndExpand(foo, bar).toString());
System.out.println(ucb.buildAndExpand(foo, bar).toUriString());
System.out.println(ucb.buildAndExpand(foo, bar).encode().toUri());
System.out.println(ucb.buildAndExpand(foo, bar).encode().toString());
System.out.println(ucb.buildAndExpand(foo, bar).encode().toUriString());
and that printed:
http://example.com/?foo=fo+o&bar=ba%20r
http://example.com/?foo=fo+o&bar=ba r
http://example.com/?foo=fo+o&bar=ba r
http://example.com/?foo=fo+o&bar=ba%20r
http://example.com/?foo=fo+o&bar=ba%20r
http://example.com/?foo=fo+o&bar=ba%20r
http://example.com/?foo=fo+o&bar=ba%20r
http://example.com/?foo=fo+o&bar=ba r
http://example.com/?foo=fo+o&bar=ba r
http://example.com/?foo=fo+o&bar=ba%20r
http://example.com/?foo=fo+o&bar=ba%20r
http://example.com/?foo=fo+o&bar=ba%20r
The space is correctly escaped in some instances, but the plus is never escaped.
I also tried UriTemplate
like this:
String foo = "fo+o";
String bar = "ba r";
UriTemplate uriTemplate = new UriTemplate("http://example.com/?foo={foo}&bar={bar}");
Map<String, String> vars = new HashMap<>();
vars.put("foo", foo);
vars.put("bar", bar);
URI uri = uriTemplate.expand(vars);
System.out.println(uri);
with the exact same result:
http://example.com/?foo=fo+o&bar=ba%20r
To put it simply, the set of exchange functions are the most general/capable methods provided by RestTemplate , so you can use exchange when none of the other methods provides a complete enough parameter set to meet your needs.
I'm starting to believe this is a bug and I reported here: https://jira.spring.io/browse/SPR-16860
Currently, my workaround is this:
String foo = "fo+o";
String bar = "ba r";
String uri = UriComponentsBuilder.
fromUriString("http://example.com/?foo={foo}&bar={bar}").
buildAndExpand(vars).toUriString();
uri = uri.replace("+", "%2B"); // This is the horrible hack.
try {
return new URI(uriString);
} catch (URISyntaxException e) {
throw new RuntimeException("UriComponentsBuilder generated an invalid URI.", e);
}
which is a horrible hack that might fail in some situations.
I think your problem here is that RFC 3986, on which UriComponents
and by extension UriTemplate
are based, does not mandate the escaping of +
in a query string.
The spec's view on this is simply:
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
query = *( pchar / "/" / "?" )
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
If your web framework (Spring MVC, for example!) is interpreting +
as a space, then that is its decision and not required under the URI spec.
With reference to the above, you will also see that !$'()*+,;
are not escaped by UriTemplate
. =
and &
are escaped because Spring has taken an "opinionated" view of what a query string looks like -- a sequence of key=value pairs.
Likewise, #[]
and whitespace are escaped because they are illegal in a query string under the spec.
Granted, none of this is likely to be any consolation to you if you just quite reasonably want your query parameters escaped!
To actually encode the query params so your web framework can tolerate them, you could use something like org.springframework.web.util.UriUtils.encode(foo, charset)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With