Disable RestTemplate escaping - spring

An existing API I have to call accepts a query parameter in the form
?key=001|ABC|123456
However RestTemplate, using its internal UriBuilder is escaping the | character to %7C.
Thus the parameter results in
?key=001%7CABC%7C123456
I've tried setting a custom UriTemplateHandler via setUriTemplateHandler, using a new DefaultUriBuilderFactory
final var uriBuilder = new DefaultUriBuilderFactory();
and disabling the encoding and parsing
uriBuilder.setEncodingMode(EncodingMode.NONE);
uriBuilder.setParsePath(false);
however another exception is thrown internally when constructing the URI
URI.create(url)
saying an illegal character is present, which is, obviously |.
How can I totally disable this behavior in Spring, using RestTemplate?
I cannot use escaped values.
Executing the same call via SoapUI, using a non-escaped URL, returns the correct response.

It seems that there is no clean way to do this with Spring classes.
In the end I've choosen for the raw HttpURLConnection, which, while not as easy-to-use, provide all the required functionality anyway.
The thing is internally, the DefaultUriBuilderFactory which is the one used to build the final URI, uses the java.net.URI class, which obey RFCs rules.
private URI createUri(UriComponents uric) {
if (encodingMode.equals(EncodingMode.URI_COMPONENT)) {
uric = uric.encode();
}
return URI.create(uric.toString());
}
As you can see, even if you disable encoding via
uriBuilder.setEncodingMode(EncodingMode.NONE);
the final result is always a call to URI.create.
You cannot escape that, if not by providing a custom UriBuilderFactory.

Related

How to handle invalid/extra special characters & = in request url-SpringBoot?

I have a Rest service where get call if I send multiple invalid/extra & and = characters then also my endpoint does not throw any error. I would like to throw back invalid request error if url contains any extra special character like & or =.
for example:
http://localhost:8080/myservice?rollNo=03456789321&school=Myschool //This is Okay for me
http://localhost:8080/myservice?rollNo=03456789321&school= //should throw error as school is not having value
http://localhost:8080/myservice?rollNo=03456789321&&&&school=Myschool
//should throw error as &&&& is multiple where it should only one
http://localhost:8080/myservice?rollNo=03456789321&= //should throw error as &= is there at end having no sence.
Note that , I am hitting these request from postman , and I have doubt that postman do something with these parameters, cause I am not able to find these extra characters in spring boot while debugging.
Any way through which i can get whole request url in my controller so that I can find out for these charecters comming?
Any built in springboot annotation is there to handle such a cases?
I got my problem solved.
After lot of research , and some observation I came to know that when you pass any number of characters among & and = in request url, the rest client tools like postman , or advanced rest client will refine the url before hitting actual server and remove those extra un-necessary characters. SO if you write multiple &&&& or == charecters in url , it will consider each extra & as blank parameter and will ignore while sending final request, only of those characters which has parameter names besides it it will taken as part of refined request.
you can see in screenshot bellow:
You can Use #RequestParam in your Spring Boot rest Controller
Something of the following
#GetMapping(value = "/myservice")
public boolean doSomething(#RequestParam("rollNo") Integer rollNo , #RequestParam("school") String school) {
doValidation(rollNo,school);
// Do Something
return true;
}
#RequestParam will make sure that your Url need to have these Params rollNo & school. Without it it will throw error.
But if you were to pass an empty string like &school= in your second example. The controller will get an empty String.
You can add a basic validation layer right before you do anything in you controller to handle this condition.

Why is Spring de-coding + (the plus character) on application/json get requests? and what should I do about it?

I have a Spring application that receives a request like http://localhost/foo?email=foo+bar#example.com. This triggers a controller that roughly looks like this:
#RestController
#RequestMapping("/foo")
public class FooController extends Controller {
#GetMapping
public void foo(#RequestParam("email") String email) {
System.out.println(email)
}
}
By the time I can access email, it's been converted to foo bar#example.com instead of the original foo+bar#example.com. According to When to encode space to plus (+) or %20? this should only happen in requests where the content is application/x-www-form-urlencoded. My request has a content type of application/json. The full MIME headers of the request look like this:
=== MimeHeaders ===
accept = application/json
content-type = application/json
user-agent = Dashman Configurator/0.0.0-dev
content-length = 0
host = localhost:8080
connection = keep-alive
Why is Spring then decoding the plus as a space? And if this is the way it should work, why isn't it encoding pluses as %2B when making requests?
I found this bug report about it: https://jira.spring.io/browse/SPR-6291 which may imply that this is fixed on version 3.0.5 and I'm using Spring > 5.0.0. It is possible that I may misinterpreting something about the bug report.
I also found this discussion about RestTemplate treatment of these values: https://jira.spring.io/browse/SPR-5516 (my client is using RestTemplate).
So, my questions are, why is Spring doing this? How can I disable it? Should I disable it or should I encode pluses on the client, even if the requests are json?
Just to clarify, I'm not using neither HTML nor JavaScript anywhere here. There's a Spring Rest Controller and the client is Spring's RestTemplate with UriTemplate or UriComponentsBuilder, neither of which encode the plus sign the way Spring decodes it.
Original Answer
You are mixing 2 things, a + in the body of the request would mean a space when header has application/x-www-form-urlencoded. The body or content of the request would be dependent on the headers but a request can just have a url and no headers and no body.
So the encoding of a URI cannot be controlled by any headers as such
See the URL Encoding section in https://en.wikipedia.org/wiki/Query_string
Some characters cannot be part of a URL (for example, the space) and some other characters have a special meaning in a URL: for example, the character # can be used to further specify a subsection (or fragment) of a document. In HTML forms, the character = is used to separate a name from a value. The URI generic syntax uses URL encoding to deal with this problem, while HTML forms make some additional substitutions rather than applying percent encoding for all such characters. SPACE is encoded as '+' or "%20".[10]
HTML 5 specifies the following transformation for submitting HTML forms with the "get" method to a web server.1 The following is a brief summary of the algorithm:
Characters that cannot be converted to the correct charset are replaced with HTML numeric character references[11]
SPACE is encoded as '+' or '%20'
Letters (A–Z and a–z), numbers (0–9) and the characters '*','-','.' and '_' are left as-is
All other characters are encoded as %HH hex representation with any non-ASCII characters first encoded as UTF-8 (or other specified encoding)
The octet corresponding to the tilde ("~") is permitted in query strings by RFC3986 but required to be percent-encoded in HTML forms to "%7E".
The encoding of SPACE as '+' and the selection of "as-is" characters distinguishes this encoding from RFC 3986.
And you can see the same behaviour on google.com as well from below screenshots
Also you can see the same behaviour in other frameworks as well. Below is an example of Python Flask
So what you are seeing is correct, you are just comparing it with a document which refers to the body content of a request and not the URL
Edit-1: 22nd May
After debugging it seems the decoding doesn't even happen in Spring. I happens in package org.apache.tomcat.util.buf; and the UDecoder class
/**
* URLDecode, will modify the source.
* #param mb The URL encoded bytes
* #param query <code>true</code> if this is a query string
* #throws IOException Invalid %xx URL encoding
*/
public void convert( ByteChunk mb, boolean query )
throws IOException
{
int start=mb.getOffset();
And below is where the conversion stuff actually happens
if( buff[ j ] == '+' && query) {
buff[idx]= (byte)' ' ;
} else if( buff[ j ] != '%' ) {
This means that it is an embedded tomcat server which does this translation and spring doesn't even participate in this. There is no config to change this behaviour as seen in the class code. So you have to live with it
SPR-6291 fixed this problem in v3.0.5 but this remains unresolved in some other cases like SPR-11047 is still unresolved. While SPR-6291's priority was Major, SPR-11047's priority is Minor.
I faced this problem when I was working on REST API in old Spring last year. There are multiple ways we can get data in Spring controller. So two of them are via #RequestParam or #PathVariable annotation
As others mentioned I think its spring's internal issue and does not specifically belong to URL encoding because I was sending data over POST request but it is somewhat encoding problem. But I also agree with others as now it remains problematic only in URL.
So there are two solutions I know:
You can use #PathVariable instead of #RequestParam because as of SPR-6291 this plus sign issue is fixed in #PathVariable and still remains open for #RequestParam as SPR-11047
My version of spring was not even accepting plus sign via #PathVariable annotation, so this is how I overcome the problem (I don't remember it step by step but it will give you hint).
In your case you can get the fields via JS and escape the plus sign before sending a request. Something like this:
var email = document.getElementById("emailField").value;
email = email.replace('+', '%2B');
If you have this request:
http://localhost/foo?email=foo+bar#example.com
then the original is foo bar#example.com. If you say the original should be foo+bar#example.com then the request should be:
http://localhost/foo?email=foo%2Bbar#example.com
So Spring is working as supposed to. Maybe on client you should check if the URI is properly encoded. The client-side URL encoding is responsible for building a correct HTTP request.
See encodeURI() if you generate the request in JavaScript or uriToString() if you generate the request in Spring.
Build your request string (the part after ?), without any encoding, with unencoded values like foo+bar#email.com, and only in the end, before actually using it in GET, encode all of it with whatever is available on the client platform. If you want to use POST then you should encode it according to the MIME type of your choice.

Spring ServletUriComponentsBuilder and square brackets in query params

I'm trying to use Spring's ServletUriComponentsBuilder to create paging next and prev links from the current request.
The problem I'm having is that the ServletUriComponentsBuilder.fromCurrentRequest() is not unencoding percent-encoded values like:
http://example.com/articles?page%5Bnumber%5D=2
The problem is uses could have called the page with unencoded square brackets like http://example.com/articles?page[number]=2 without any problems.
Spring Data is accepting both variants (both unencoded square brackets and encoded square brackets) in it's pageable argument resolver.
This to the fact that under water the Coyote web request get parameter is used which contains the unencoded param names.
Also Spring's #RequestParam("page[number]") accepts without any problem the encoded request like http://example.com/articles?page%5Bnumber%5D=2.
From the server side I always want to return percent encoded url's as per RFC 3986.
But there does not seem a way to this as the UriComponents query params might contain both encoded en uncoded names. Because to that, if I would call encode() on the builder the already encoded query params get encoded another time, but if would contain unencoded names toURI() will fail as an unencoded [ is not allowed.
Note that the url's might contain multiple query params besides paging, e.g. for filtering.
A request could come in like:
http://example.com/articles?filter[category]=food
And would return a response with a encoded next link like:
http://example.com/articles?page%5Bnumber%5D=2&filter%5Bcategory%5D=food
My workaround it to ignore ServletUriComponentsBuilder and simply get the request url and do so custom regexp replacing.
I know this is an older question and you also found a workaround. But did you try to use ServletUriComponentsBuilder's build() method?
Some kind of the following:
ServletUriComponentsBuilder.fromCurrentRequest().build().toUriString();
I had some issues when handling JSON Strings and this helped.

Handling forward slashes spring mvc rest

I have a rest controller setup like below
#RequestMapping(value = {"getDataFromSpaceForType/{gridName}/{spaceName}/{dataType}/{fieldNames}/{criteria}"}, method = GET, produces = "application/json")
public EntriesForTypeName getDataFromSpaceForType(#PathVariable Map<String, String> dataRequestVariables) throws Exception {
The last field criteria can contain multiple forward slashes. I am unable to handle such scenario. It breaks with 404 error
Also i dont know how many slashes might come, so it becomes impossible to create multiple methods.
If you know that your URL may contain slashes, all you have to do is to encode the URL before you send/use it.
If your front-end is angularJS then you can do it like this:
How to generate url encoded anchor links with AngularJS?
Or if you use jquery then:
URL Encode a string in jQuery for an AJAX request

Stop URITemplate expansion when using Spring RESTTemplate

I am using the Spring RestTemplate to make calls to a Apache Solr index. I form a request string manually and don't supply any intentional {variable_name} template expansion variables. Part of the query is the term {!lucene q.op=OR}. Unfortunately this gets processed by the URITemplate engine as part of a restTemplate.getForObject call.
Ideally i would like to stop this processing. Is there away of escaping the { } characters so that URITemplate doesn't process them? I have tried encoding the characters but RestTemplate assumes a non-encoded string so they are encoded twice and cause a 400: Bad Request on the backend.
Sample URL:
http://localhost/solr/select?q={!lucene
q.op=OR}se_genbanklocus:*
se_gb_create:* se_gb_update:*
se_clone_name:*
se_sample_tissue:*&facet=true&facet.limit=3&facet.mincount=1&facet.field=se_sample_tissue&facet.field=se_sample_tissue_name&facet.field=se_sample_tissue_code&facet.field=se_sample_tissue_class&facet.field=se_nuc_acid_type&facet.field=ssam_sample_georegion&start=0&rows=10
I've found a work around in which i can use the template to expand one variable which contains the offending {!lucene q.op=OR}
restTemplate.getForObject(solrServer+"select?{query}" , String.class, requestString );
The problem here is that you're using RestTemplate for something it's not designed for. The sample URL you gave is not a REST-style URL, it's just a mass of query parameters, using encoded characters that you're not going to find in a REST scheme, hence the difficulty with unwanted substitutions.
How about using the overloaded method that accepts a URI?

Resources