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
    }
}
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());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With