Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rest Template custom exception handling

I am using some REST endpoints from an external API and I am using the Rest Template interface for this purpose. I would like to be able to throw custom app exceptions when I receive certain HTTP status codes from these calls. In order to achieve it, I am implementing the ResponseErrorHandler interface as follows:

public class MyCustomResponseErrorHandler implements ResponseErrorHandler {

    private ResponseErrorHandler myErrorHandler = new DefaultResponseErrorHandler();

    public boolean hasError(ClientHttpResponse response) throws IOException {
        return myErrorHandler.hasError(response);
    }

    public void handleError(ClientHttpResponse response) throws IOException {
        String body = IOUtils.toString(response.getBody());
        MyCustomException exception = new MyCustomException(response.getStatusCode(), body, body);
        throw exception;
    }

}

public class MyCustomException extends IOException {

    private HttpStatus statusCode;

    private String body;

    public MyCustomException(String msg) {
        super(msg);
        // TODO Auto-generated constructor stub
    }

    public MyCustomException(HttpStatus statusCode, String body, String msg) {
        super(msg);
        this.statusCode = statusCode;
        this.body = body;
    }

    public HttpStatus getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(HttpStatus statusCode) {
        this.statusCode = statusCode;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

}

Finally, this is the client code (irrelevant code omitted):

public LoginResponse doLogin(String email, String password) {
    HttpEntity<?> requestEntity = new HttpEntity<Object>(crateBasicAuthHeaders(email,password));
    try{
        ResponseEntity<LoginResponse> responseEntity = restTemplate.exchange(myBaseURL + "/user/account/" + email, HttpMethod.GET, requestEntity, LoginResponse.class);
        return responseEntity.getBody();
    } catch (Exception e) {
        //Custom error handler in action, here we're supposed to receive a MyCustomException
        if (e instanceof MyCustomException){
            MyCustomException exception = (MyCustomException) e;
            logger.info("An error occurred while calling api/user/account API endpoint: " + e.getMessage());
        } else {
             logger.info("An error occurred while trying to parse Login Response JSON object");
        }
    }
    return null;
}

My app context:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:spring="http://camel.apache.org/schema/spring"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

    <!-- Rest template (used in bridge communication) -->
    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
        <property name="errorHandler" ref="myCustomResponseErrorHandler"></property>
    </bean>

    <!-- Bridge service -->
    <bean id="myBridgeService" class="a.b.c.d.service.impl.MyBridgeServiceImpl"/>

    <!-- Bridge error handler -->
    <bean id="myCustomResponseErrorHandler" class="a.b.c.d.service.handlers.MyCustomResponseErrorHandler"/>

</beans>

I suspect I am not correctly understanding the behaviour of this custom error handling. Every single rest template method might throw a RestClientException, which following the exception hierarchy, is a subclass of RuntimeException and not IOException, which is thrown in the custom response error handler, i.e.: I can't catch my custom exception in the rest template method calls.

Any clues on how I could catch these exceptions? Spring RestTemplate invoking webservice with errors and analyze status code is highly related but from my point of view experiences the same problem, although it was proposed as a solution.

[1]:

like image 793
jarandaf Avatar asked Jan 29 '14 11:01

jarandaf


People also ask

How do you handle an exception from RestTemplate?

Default Error Handling By default, the RestTemplate will throw one of these exceptions in the case of an HTTP error: HttpClientErrorException – in the case of HTTP status 4xx. HttpServerErrorException – in the case of HTTP status 5xx. UnknownHttpStatusCodeException – in the case of an unknown HTTP status.

What does the RestTemplate handle?

Class RestTemplate. Synchronous client to perform HTTP requests, exposing a simple, template method API over underlying HTTP client libraries such as the JDK HttpURLConnection , Apache HttpComponents, and others.

How do I send a custom error message in REST API Spring boot?

The most basic way of returning an error message from a REST API is to use the @ResponseStatus annotation. We can add the error message in the annotation's reason field. Although we can only return a generic error message, we can specify exception-specific error messages.


1 Answers

You've made your custom Exception extend from IOException

public class MyCustomException extends IOException {

The ResponseErrorHandler#handleError() method is invoked from RestTemplate#handleResponseError(..) which is invoked by RestTemplate#doExecute(..). This root invocation is wrapped in a try-catch block which catches IOException and rethrows it wrapped in a ResourceAccessException, which is a RestClientException.

One possibility is to catch the RestClientException and get its cause.

Another possibility is to make your custom Exception be a sub type of RuntimeException.

like image 62
Sotirios Delimanolis Avatar answered Sep 20 '22 16:09

Sotirios Delimanolis