Description
Vsevolod Kalinin opened SPR-16002 and commented
After working with that for quite some time I have a strong feeling that something is wrong with UriComponentsBuilder
query params encoding/decoding. Will try to explain it step by step now + my vision to the problem (still with hope of doing something wrong/missing something :)). Will use fromHttpUrl(..) in examples, but the behavior is the same for all from*(..) methods of all *UriComponentsBuilder
classes (and it hurt even more when using something like fromHttpRequest(..)).
Use cases
So, consider I want to take current request URL, modify it (add/remove query params, etc.) and eventually use it somewhere else. Again there are fromHttpUrl(..) calls below for simplicity; IRL they are fromHttpRequest(..), etc.
- h3. Double-encoded query params from original URL
UriComponentsBuilder ucb = UriComponentsBuilder.fromHttpUrl("http://host.com?name=Tom%26Jerry");
ucb.queryParam("album", "S&M");
ucb.toUriString(); // http://host.com?name=Tom%2526Jerry&album=S%26M
It returns http://host.com?name=Tom%2526Jerry&album=S%26M
which is wrong (see double-encoded initial name param).
I was only able to solve the above with something like:
UriComponentsBuilder ucb = UriComponentsBuilder.fromHttpUrl("http://host.com?name=Tom%26Jerry");
ucb.queryParam("album", java.net.URLEncoder.encode("S&M"));
ucb.build(true).toUriString();
Now it returns the correct http://host.com?name=Tom%26Jerry&album=S%26M
, but the whole thing got a bit ugly.
- h3. application/x-www-form-urlencoded whitespaces
Now the request has application/x-www-form-urlencoded whitespace (converted to +) and taught by the bitter experience above we useucb.build(true).toUriString()
:
UriComponentsBuilder ucb = UriComponentsBuilder.fromHttpUrl("http://host.com?name=Tom+Jerry");
ucb.queryParam("album", "S&M");
ucb.build(true).toUriString();
But it's java.lang.IllegalArgumentException now - UriComponentsBuilder doesn't like "+".
Reverting to the initial approach doesn't work either:
UriComponentsBuilder ucb = UriComponentsBuilder.fromHttpUrl("http://host.com?name=Tom+Jerry");
ucb.queryParam("album", "S&M");
ucb.toUriString();
results in http://host.com?name=Tom%2BJerry&album=S%26M
- note "+" being encoded now.
The only option is to do an ugly workaround mentioned in #14805.
Probable solution
IMHO it would have been much more convenient if UriComponentsBuilder
decoded query params while parsing (presumably with application/x-www-form-urlencoded support; at least when creating from a request where MIME type should be available). This way both cases above are solved + a test
final String requestHttpUrl = <any valid request URL>;
final String processedHttpUrl = UriComponentsBuilder.fromHttpUrl(requestHttpUrl).toUriString();
assertEquals(processedHttpUrl, requestHttpUrl);
will pass for any valid URL which is what I'd expect. Although backward compatibility concern won't make it happen easily...
Looking forward for help with this one :D
Affects: 4.3.11
Issue Links:
- UriComponentBuilder doesn't work with encoded HTTP URL having '+'. [SPR-14828] #19394 UriComponentBuilder doesn't work with encoded HTTP URL having '+'. ("duplicates")