After upgrading to Spring Boot 2.1.8.RELEASE from 1.5.x, QueryParams for HTTP Get requests with + sign are getting encoded to spaces now - spring

Before the upgrade, we had a #RestController controller end point with HTTP GET:
#RequestMapping(value = "/{dashboardDefinitionId}/widgets/{widgetId}", method = { RequestMethod.GET })
public ResponseEntity<JsonSerializer> getWidgetReportData(#PathVariable String dashboardDefinitionId,
#PathVariable String widgetId, ReportFilter reportFilter) { ... }
The ReportFilter has a startDate and endDate variables that used to be deserialized fine:
private Date startDate;
private Date endDate;
The UI was passing date strings with + signs encoding (%2b) in it for timezone information.
prior to the upgrade, the application was deserializing the strings fine into Date instances. It was encoding the %2b to '+' by the time it got to the controller and worked fine. Now, after the upgrade, it's failing bc it's seeing a space ' ' in the date instead of the + with additional timezone info in the date string.
My main question is, why is spring decoding query params, and then reencoding them somewhere downstream incorrectly and translating + in query params to spaces.
This would be an issue anywhere when then the UI is creating HTTP gets with query params with + in it, such as email addresses.

Related

RepresentationModelProcessor nullpointer having mandatory field in controller spring 2.7.4

I have a class implementing RepresentationModelProcessor that's adding links to a resource:
linkTo(methodOn(MyController.class).findMyElements(file.getId(), null, null))
.withRel("filterFiles"))
The controller looks like this:
#RequestMapping(method = { POST, GET }, value = "/myFiles/{fileId}/filterFiles")
public List<FileDto> findFilterFiles( #PathVariable("fileId") final Long fileId,
#RequestParam(name = "fileType") final Long fileType,
#RequestBody(required = false) final FileFilterDto filter)
In Spring 2.6.4, it used to work. Now, I've tried upgrading to 2.7.4 and it does not work anymore. I've tracked the issue down and it seems like the required request param must not be null anymore.
I'm getting a null pointer exception with text like this:
Illegal character in query at index 93: http://localhost:8080/files/10013/filterFiles?fileType={fileType}
with the index pointing to '=' of fileType={fileType} .
Is it a bug? If yes - how can I fix it? Passing a constant number fixes the null-pointer exception:
linkTo(methodOn(MyController.class) .findMyElements(file.getId(), 1L, null))
.withRel("filterFiles"))
but it leads to incorrect code.
I've raised an issue here: https://github.com/spring-projects/spring-hateoas/issues/1872 but it might be the wrong repository after all.

Spring4 #RequestBody required doesn't work

I've upgrade Spring version from 4.0.x to 4.2.3 (4.2.4 just now), suddenly, "required = false" property from #RequestBody annotation does not work as expected (as it did before the version change).
Server response with HttpStatus 415 - Unsupported Media Type.
The controller method (same for every Spring versions).
#RequestMapping(value = "/entity/{entityId}/{from}/{size}", method = RequestMethod.POST)
#ResponseBody
#JsonView(ToShowIn.App.class)
public ResponseEntity<?> getActiveEntityPaged(
#PathVariable int entityId, #PathVariable int from, #PathVariable int size,
#RequestBody(required = false) EntityFilterParam entityFilterParam) {
...
}
Really, it's not problem because from the client, avoiding send null, for example, setting empty EntityFilterParam instance is enough to fix it. But I would like to understand if this bothering issue is because a wrong concept from me or rather is due for a bug from the new Spring versions.
UPDATE#1
Versions
<spring-version>4.2.4.RELEASE</spring-version>
<jackson-2-version>2.3.2</jackson-2-version>
Request is from Android device, working with spring-android library (AKA RestTemplate).
Generic method, works on 4.0.x version, where EntityFilterParam can be null.
super.doRestList(
new HttpEntity<EntityFilterParam>(EntityFilterParam, this.getHttpHeaders()),
HttpMethod.POST,
urlBuilder);
On 4.2.3 and 4.2.4, to fix the issue, EntityFilterParam can't be null. -> HttpStatus 415 - Unsupported Media Type. To fix it.
super.doRestList(
new HttpEntity<EntityFilterParam>((EntityFilterParam != null) ? EntityFilterParam : new EntityFilterParam(), this.getHttpHeaders()),
HttpMethod.POST,
urlBuilder);
Inside doRestList, RestTemplate request is performed.
final ResponseEntity<E[]> responseEntity =
(ResponseEntity) super.getRestTemplate()
.exchange(urlBuilder.getURL(), httpMhetod,
requestEntity, this.entityArray.getClass());
Where, urlBuilder.getURL() (URL string, here #PathVariables are working fine) and this.getHttpHeaders() return the next HttpHeaders configuration.
RestFactory.httpHeaders = new HttpHeaders();
RestFactory.httpHeaders.setAuthorization(this.getAuthorization(user));
RestFactory.httpHeaders.set(HttpHeaders.CONNECTION, "Close"); // Switch off keep-alive.
RestFactory.httpHeaders.set(HttpHeaders.ACCEPT_LANGUAGE, "en-US,en;q=0.8");
RestFactory.httpHeaders.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
super.getRestTemplate() is the singleton method which returns the RestTemplate instance set with my own CloseableHttpClient configuration.
I know HttpHeaders.CONTENT_TYPE for application/json is missing, but is consciously, in fact, it works when the http body payload is not empty, and fails when it is.
When the error occurs, 415 is performed before the flow of the request goes inside the controller method, therefore, any operation is doesn't performed.
UPDATE#2
Another interesting point, Jackson2 serializes the null value as "null" string, then the body payload is not being null, is filled with "null".
I've tried reproducing this exact issue, without much success.
Do you confirm that with a curl command the problem still exists?
Something like curl -vvv -X POST https://example.com/entity/12/50/10 (if so, paste here the output)
I'm not 100% about this for RestTemplate in spring-android, but if no content type is provided, the template adds by default a "Content-Type: application/x-www-form-urlencoded". If this is the case, configuring a "DEBUG" log level for "org.springframework.web" should show something like
[INFO] [talledLocalContainer] org.springframework.web.HttpMediaTypeNotSupportedException:
Content type 'application/x-www-form-urlencoded' not supported
I think you should set the Content-Type anyway. You mentioned this fails when you do - could you elaborate a bit on that?

Spring DateTimeFormat.ISO.Date preconfigured pattern returns syntactically incorrect

I am trying to pass a date in my URL in the format: 2014/12/12
So my url looks like:
http://192.0.0.1/api/getdata?date=2014-12-12
My controller method looks like:
public Iterable<Object> getObject(
#RequestParam(value = "date", required = false)
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date date)
I am getting the tomcat error
<b>description</b>
<u>The request sent by the client was syntactically incorrect.</u>
I thought if I was using a pre-configured date pattern, there would no be an issue with parsing the date?
The request sent by the client was syntactically incorrect using #DateTimeFormat
I have looked at the question above which is similar. But the question is using MM/yy which isn't preconfigured. Do I still need a custom date deserializer anyway?
Spring supports rest URL, so your the date format you want to pass will be divided into 3 parts.
It will not be possible to send the date in url using this format.You need to change the date format which you want to pass.
The format yyyy/MM/dd is a bad format to be passing the date, for the reason Sambhunath mentioned. However if you have to use this format, you should register your binder
#InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
and remove the #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) annotation, from the argument.
Another thing is that from your question is not clear if you want to use the yyyy-MM-dd format, in which case your code is fine, and the issue your having is the error in your request. Remove the & from
http://192.0.0.1/api/getdata?&date=2014-12-12
so make it
http://192.0.0.1/api/getdata?date=2014-12-12

Rest query params in Java

I need to do some queries against my datastore in Java but I can't seem to get the parameters syntax right. I tried like this:
String params = "?Active=1";
String urlString = "https://api.parse.com/1/classes/Cars" + params;
Or as per the document here:
String params = "where={Active:1}";
But both ways generate an exception.
If I don't do the query and simply try to get all the objects with this request string:
String urlString = "https://api.parse.com/1/classes/Cars"
everything works fine. So the problem is definitely the params sequence. So is there a way to do Prase.com rest queries in Java?
EDIT: adding the exception string in response to a request from the first comment:
java.io.IOException: Server returned HTTP response code: 400 for URL: https://api.parse.com/1/classes/Cars?where={Active:1}
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1838)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1439)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
I should also note that when I use the regular http syntax, as in
params = "?Dealer=asdf";
the query comes back with all the objects, as if the parameter wasn't there.
Here are a couple of working examples for the params string:
String params = "where={\"objectId\":\"ldl49l3kd98\"}";
String params = "where={\"CompanyName\":\"BMW\", \"Price\":{\"$gte\":29000,\"$lte\":49000}}";
And if you need non English characters, like I do, encode the param string like this:
params = URLEncoder.encode(params, "UTF-8");

Spring RestTemplate is not preserving accent characters

I have been trying to use accent characters in URL to call SOLR.
My Url looks like this:
"http://host:8983/solr/principal/select?q=**name:%22Michaël.e%22**"
When fire the URL from browser I get the correct result but when try from RestTempalte.exchange(URI,HttpMethod.GET, entity, String.class)
The log I see on SOLR is showing the accent characters being coverted to "?" as shown below
q=(name:"Micha?.e")
I have set RestTemple request charSet to "UTF-8" it still does the same.
My SOLR is running on Jetty.
You can try to encode HTML characters before calling RestTemplate using URLEncoder
String baseUri = "http://host:8983/solr/principal/select?q=**name:%22";
// TODO get name from somewhere
String name = "Michaël.e";
String encodedName= = URLEncoder.encode(name, "UTF-8");
RestTempalte.exchange(baseUri + encodedName + "%22**",HttpMethod.GET, entity, String.class);

Resources