Using RestTemplate in Spring. Exception- Not enough variables available to expand

SpringResttemplate

Spring Problem Overview


I am trying to access the contents of an API and I need to send a URL using RestTemplate.

String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort={\"price\":\"desc\"}";

OutputPage page = restTemplate.getForObject(url1, OutputPage .class);

But, I am getting the following error.

Exception in thread "main" java.lang.IllegalArgumentException: Not enough variable values available to expand '"price"'
at org.springframework.web.util.UriComponents$VarArgsTemplateVariables.getValue(UriComponents.java:284)
at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:220)
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:317)
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:46)
at org.springframework.web.util.UriComponents.expand(UriComponents.java:162)
at org.springframework.web.util.UriTemplate.expand(UriTemplate.java:119)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:501)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:239)
at hello.Application.main(Application.java:26)

If I remove the sort criteria, it is working properly. I need to parse the JSON using sort criteria. Any help will be much appreciated.

Thanks

Spring Solutions


Solution 1 - Spring

The root cause is that RestTemplate considers curly braces {...} in the given URL as a placeholder for URI variables and tries to replace them based on their name. For example

{pageSize}

would try to get a URI variable called pageSize. These URI variables are specified with some of the other overloaded getForObject methods. You haven't provided any, but your URL expects one, so the method throws an exception.

One solution is to make a String object containing the value

String sort = "{\"price\":\"desc\"}";

and provide a real URI variable in your URL

String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort={sort}";

You would call your getForObject() like so

OutputPage page = restTemplate.getForObject(url1, OutputPage.class, sort);

I strongly suggest you do not send any JSON in a request parameter of a GET request but rather send it in the body of a POST request.

Solution 2 - Spring

If the solution suggested by sotirios-delimanolis is a little difficult to implement in a scenario, and if the URI string containing curly braces and other characters is guaranteed to be correct, it might be simpler to pass the encoded URI string to a method of RestTemplate that hits the ReST server.

The URI string can be built using UriComponentsBuilder.build(), encoded using UriComponents.encode(), and sent using RestTemplate.exchange() like this:

public ResponseEntity<Object> requestRestServer()
{
    HttpEntity<?> entity = new HttpEntity<>(requestHeaders);
	UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(rawValidUrl)
			.queryParams(
					(LinkedMultiValueMap<String, String>) allRequestParams);
	UriComponents uriComponents = builder.build().encode();
	ResponseEntity<Object> responseEntity = restTemplate.exchange(uriComponents.toUri(), HttpMethod.GET,
			entity, String.class);
    return responseEntity;
}

Building, encoding, and extracting URI have been seperated out for clarity in the above code snippet.

Solution 3 - Spring

You can URL encode the parameter values:

String url1 = "http://api.example.com/Search?key=52ddafbe3ee659bad97fcce7c53592916a6bfd73&term=&limit=100&sort=";

org.apache.commons.codec.net.URLCodec codec = new org.apache.commons.codec.net.URLCodec();
url1 = url1 + codec.encode("{\"price\":\"desc\"}");
OutputPage page = restTemplate.getForObject(url1, OutputPage.class);

Solution 4 - Spring

You can set a specific UriTemplateHandler in your restTemplate. This handler would just ignore uriVariables :

UriTemplateHandler skipVariablePlaceHolderUriTemplateHandler = new UriTemplateHandler() {
    @Override
    public URI expand(String uriTemplate, Object... uriVariables) {
        return retrieveURI(uriTemplate);
    }

    @Override
    public URI expand(String uriTemplate, Map<String, ?> uriVariables) {
        return retrieveURI(uriTemplate);
    }

    private URI retrieveURI(String uriTemplate) {
        return UriComponentsBuilder.fromUriString(uriTemplate).build().toUri();
    }
};

restTemplate.setUriTemplateHandler(skipVariablePlaceHolderUriTemplateHandler);

Solution 5 - Spring

You can simply append a variable key to the URL and give the value using the restTemplate.getForObject() method.

Example:

String url = "http://example.com/api?key=12345&sort={data}";
String data="{\"price\":\"desc\"}";

OutputPage page = restTemplate.getForObject(url, OutputPage.class, data);

Solution 6 - Spring

You can encode url before using RestTemplate

URLEncoder.encode(data, StandardCharsets.UTF_8.toString());

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser3311403View Question on Stackoverflow
Solution 1 - SpringSotirios DelimanolisView Answer on Stackoverflow
Solution 2 - SpringnoobView Answer on Stackoverflow
Solution 3 - SpringThorView Answer on Stackoverflow
Solution 4 - Springc2mView Answer on Stackoverflow
Solution 5 - SpringSomnath SinghView Answer on Stackoverflow
Solution 6 - SpringZongguangView Answer on Stackoverflow