Reputation: 108
I encountered an interesting problem while working on some integration tests.
I have a rest endpoint which has some query parameters which are time stamps specified in ISO-8601 (ex: 2020-07-08T23:54:36.931159+03:00). The dates are formatted using java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME
I create the request address by using:
//Query params are supplied in a Map<String,String>
String baseURL="http://localhost:" + this.port + uri
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseURL);
if (params != null) {
for (final Entry<String, String> entry : params.entrySet()) {
builder.queryParam(entry.getKey(), entry.getValue());
}
}
This will generate the following address:
http://localhost:8090/remainingAddress?fromDate=2020-07-08T23:54:36.834869+03:00&toDate=2020-07-08T23:54:36.931159+03:00
The problem is that the "+" sign was not encoded and stayed as it was originally present in the Map. On the receiving end the parameters are decoded thus resulting in the following date "020-07-08T23:54:36.834869 03:00" (notice the space instead of the +) and because of this when I attempt to parse the date using the same formatter it fails.
I tried encoding the parameters with java.net.URLEncoder.encode(String, Charset) before I add the value to the builder:
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseURL);
if (params != null) {
for (final Entry<String, String> entry : params.entrySet()) {
var encoded=URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8);
builder.queryParam(entry.getKey(), encoded);
}
}
However this introduces "%" as a special character which actually triggers encoding in the builder thus the date parameter is encoded 2 times.
input string: 2020-07-09T00:14:15.230699+03:00
result after first encoding: 2020-07-09T00%3A14%3A15.230699%2B03%3A00
final result: http://localhost:8090/remainingAddress?fromDate=2020-07-09T00%253A14%253A15.230699%252B03%253A00&toDate=2020-07-09T00%253A14%253A15.31158%252B03%253A00
Upon execution of the request,I receive the following input when trying to parse the date: 2020-07-09T00%253A14%253A15.230699%252B03%253A00 (quite a mess).
Note this project uses Spring Boot V2.5.5.
My question is how should I offer the query parameters so they will be encoded correctly? Or perhaps this builder is not meant to be used with special characters?
Upvotes: 2
Views: 1407
Reputation: 4430
NOTE: This is Kotlin implementation
I came across this issue recently,
Where one of my endpoints have range limits as queryparam with type OffsetDateTime.
Which is sometimes like a UTC timestamp e.g., 2021-01-13T02:00Z
or with a zone offset like 2021-01-13T13:00+11:00
where both equals to 2021-01-13T13:00
LocalDateTime of ZoneId Australia/Melbourne
.
Initially, I tried to build uri like:
val uri = UriComponentsBuilder.fromUriString(baseApi.url)
.path("/v1/products/${productId}/orders")
.queryParam("from", from)
.queryParam("to", to)
.queryParam("type", type)
.build().encode().toUri()
Which works fine with UTC timestamps but not encoding timestamps with offset. Like in your case.
Then this is what fixed for me:
val uri = UriComponentsBuilder.fromUriString(baseApi.url)
.path("/v1/products/${productId}/orders")
.queryParam("from", "{from}")
.queryParam("to", "{to}")
.queryParam("type", type)
.encode().buildAndExpand(from, to).toUri()
This helps to inject OffsetDateTime as variable into string while buildAndExpand. String interpolation here fixes any uriString issues by adding those special escape characters.
Upvotes: 1