'spring resttemplate url encoding
I try to do a simple rest call with springs resttemplate:
private void doLogout(String endpointUrl, String sessionId) {
template.getForObject("http://{enpointUrl}?method=logout&session={sessionId}", Object.class,
endpointUrl, sessionId);
}
Where the endpointUrl variable contains something like service.host.com/api/service.php
Unfortunately, my call results in a org.springframework.web.client.ResourceAccessException: I/O error: service.host.com%2Fapi%2Fservice.php
So spring seems to encode my endpointUrl string before during the creation of the url. Is there a simple way to prevent spring from doing this?
Regards
Solution 1:[1]
Depends on which version of Spring you're using. If your version is too old, for example, version 3.0.6.RELEASE, you'll not have such facility as UriComponentsBuilder with your spring-web jar.
What you need is to prevent Spring RestTemplate from encoding the URL. What you could do is:
import java.net.URI;
StringBuilder builder = new StringBuilder("http://");
builder.append(endpointUrl);
builder.append("?method=logout&session=");
builder.append(sessionId);
URI uri = URI.create(builder.toString());
restTemplate.getForObject(uri, Object.class);
I tested it with Spring version 3.0.6.RELEASE, and it works.
In a word, instead of using restTemplate.getForObject(String url, Object.class), use restTemplate.getForObject(java.net.URI uri, Object.class)
See the rest-resttemplate-uri section of the Spring document
Solution 2:[2]
Looks like I found best native way (up-to-date) solution:
- Do not pass encoded url string as parameter to
RestTemplate.exchange() - Use URI object instead. Use
UriComponentsBuilderto construct URI.
See (simplified) example below:
String instanceUrl = "https://abc.my.salesforce.com"
HttpEntity<String> entity = new HttpEntity<>(headers);
UriComponents uriComponents =
UriComponentsBuilder.fromHttpUrl(instanceUrl)
.path("/services/data/v45.0/query/")
.queryParam("q", String.format(sqlSelect, id))
.build();
ResponseEntity<OpportunityLineItem> responseEntity =
restTemplate.exchange(
uriComponents.toUri(), HttpMethod.GET,
entity, OpportunityLineItem.class);
// Wrong! URI string will be double encoded
/*
ResponseEntity<OpportunityLineItem> responseEntity =
restTemplate.exchange(
uriComponents.toUriString(), HttpMethod.GET,
entity, OpportunityLineItem.class);
*/
This way you will not get issue with double encoding.
Solution was found while debugging SalesForce REST client, based on Spring RestTemplate client (including SOQL queries).
Solution 3:[3]
You can use the overloaded variant that takes a java.net.URI instead public T getForObject(URI url, Class responseType) throws RestClientException
From Spring's own documentation
UriComponents uriComponents =
UriComponentsBuilder.fromUriString("http://example.com/hotels/{hotel}/bookings/{booking}").build()
.expand("42", "21")
.encode();
URI uri = uriComponents.toUri();
Solution 4:[4]
Full example with headers, body, for any HttpMethod and ResponseType could look like:
String url = "http://google.com/{path}?param1={param1Value}¶m2={param2Value}";
Object body = null;
HttpEntity request = new HttpEntity(body, new HttpHeaders());
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("path", "search");
uriVariables.put("param1Value", "value1");
uriVariables.put("param2Value", "value2");
ResponseEntity<Void> responseEntity = restTemplate.exchange(url, HttpMethod.POST, request, Void.class, uriVariables)
//responseEntity.getBody()
Actually, it will use the same UriTemplate and expand method
Solution 5:[5]
Apparently there is a better way to do this by calling build(true) of class UriComponentsBuilder:
private void doLogout(String endpointUrl, String sessionId) {
String url = "http://" + endpointUrl +"?method=logout&session=" + + URLEncoder.encode(sessionId, "UTF-8");
URI uri = UriComponentsBuilder.fromUriString(url.toString()).build(true).toUri();
template.getForObject(uri, Object.class,
endpointUrl, sessionId);
}
This method tells URIComponentsBuilder not to encode while creating URI.
Solution 6:[6]
Best way to do is with UriComponentsBuilder:
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
builder.queryParam("some_param", "param with space for encoding");
template.getForObject(builder.encode().build().toUri(), Object.class, headers);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Andrii Abramov |
| Solution 2 | |
| Solution 3 | Not a code monkey |
| Solution 4 | |
| Solution 5 | Tabish Khan |
| Solution 6 | Tomislav Brabec |
