Java – RestTemplate ClientHttpResponse.getBody() throws I/O Error

javaresttemplatespring

I'm using Spring RestTemplate to make RESTful calls. I'm also using a custom ClientHttpRequestInterceptor to log the request and response for debugging purposes.

In order to read the response multiple times (once for the logging, once for processing) I use a BufferingClientHttpRequestFactory. Here's the setup:

ClientHttpRequestInterceptor ri = new LoggingRequestInterceptor();
List<ClientHttpRequestInterceptor> ris = new ArrayList<ClientHttpRequestInterceptor>();
ris.add(ri);
restTemplate.setInterceptors(ris);
restTemplate.setRequestFactory(new InterceptingClientHttpRequestFactory(
        new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()) , ris));

I'm testing this system right now against a request that returns a 422 response and am having problems. From within the intercept method of my custom ClientHttpRequestInterceptor:

ClientHttpResponse response = execution.execute(request, body);
if(response.getBody() != null) {
    logger.trace(IOUtils.toString(response.getBody(), "UTF-8"));
}

The response.getBody() throws an exception:

org.springframework.web.client.ResourceAccessException: I/O error:
Server returned HTTP response code: 422 for URL:
https://testurl.com/admin/orders/564/fulfill.json;
nested exception is java.io.IOException: Server returned HTTP response
code: 422 for URL:
https://testurl.com/admin/orders/564/fulfill.json
at
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:461)
~[spring-web-3.1.2.RELEASE.jar:3.1.2.RELEASE] at
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:409)
~[spring-web-3.1.2.RELEASE.jar:3.1.2.RELEASE]

Not sure why this happens, but I turned on the debugger and I set up a watch expression for request.getBody(). The call to request.getBody() being made in that context before it gets to my actual code fixes the error.

Best Answer

The IOException thrown from sun.net.www.protocol.http.HttpURLConnection which RestTemplate uses by default. Looking the source in grepcode, getInputStream() throws IOException if the HTTP status code is 4xx (for 404 or 410 it throws a more specific FileNotFoundException)

To get around this, you need to supply a different HttpURLConnection implementation via the ClientHttpRequestFactory that is used as a constructor parameter to RestTemplate, or injected into it with the requestFactory property. For example

 HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
 RestTemplate restTemplate = new RestTemplate(factory);

Doing this via Spring injection is an exercise left for the reader :-)

Related Topic