Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IOException coming out of Spring RestTemplate Not picked up by custom ResponseErrorHandler

Tags:

java

rest

spring

I'm using Spring's RestTemplate to do some API calls to a third party service.

Everything works as expected when the response comes back as a 200 SUCCESS.

However, when the response is a 400-level error, I'm getting an IOException from deep in the belly of the RestTemplate code.

This is frustrating, because I've actually written my own implementation of ResponseErrorHandler, that checks for certain non-200 errors and does something of my choosing (logging, reformatting, etc.)

On every other third party API, my implementation of ResponseErrorHandler works, but for this server in particular, all non-200 errors result in this IOException, and my custom implementation is never reached.

Note: Curl seems to return the error normally when I try hitting this third-party endpoint.

Is this a Spring bug? Am I doing something stupid? Ideas for how to workaround?

Relevant code

Stack Trace of exception:

Caused by: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://third-party.com/api/v2/some-resource": Server returned HTTP response code: 400 for URL: https://third-party.com/api/v2/some-resource; nested exception is java.io.IOException: Server returned HTTP response code: 400 for URL: https://third-party.com/api/v2/some-resource
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:743) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:669) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:607) ~[spring-web-5.1.3.RELEASE.jar:5.1.3.RELEASE]
at org.myowncode.commons.client.RestClient.send(RestClient.java:74) ~[classes/:na]

Usage of my custom ResponseErrorHandler

this.restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
restTemplate.setErrorHandler(new LoggingResponseErrorHandler());

Example of my custom ResponseErrorHandler

(this should be irrelevant, because the above happens regardless of whether I've registered this or not)

public class LoggingResponseErrorHandler implements ResponseErrorHandler {

    private static final Logger logger = LoggerFactory.getLogger(LoggingResponseErrorHandler.class);

    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        // do my own thing
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        // do my own thing
    }

}
like image 332
Daniel Patrick Avatar asked Oct 29 '25 04:10

Daniel Patrick


1 Answers

I suspect this is connected with the use of BufferingClientHttpRequestFactory. A SimpleBufferingClientHttpRequest appears to let the IOException escape its executeInternal() method whereas the non-buffered version, SimpleStreamingClientHttpRequest, catches and ignores it which will presumably allow you to handle the response.

    @Override
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
    try {
        if (this.body != null) {
            this.body.close();
        }
        else {
            SimpleBufferingClientHttpRequest.addHeaders(this.connection, headers);
            this.connection.connect();
            // Immediately trigger the request in a no-output scenario as well
            this.connection.getResponseCode();
        }
    }
    catch (IOException ex) {
        // ignore
    }
    return new SimpleClientHttpResponse(this.connection);
}

try creating your restTemplate like this:

this.restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory());
like image 143
AndyD Avatar answered Oct 30 '25 19:10

AndyD