RestTemplate Error Handling Example

In this tutorial, we’ll implement a RestTemplate Error Handling Example. But before moving ahead, let’s see why is there a need to do this? The answer is:

It becomes very tedious to handle exceptions through try/catch blocks as applications scale. An Efficient way to avoid this hassle is to implement a reusable error handler that manages all the errors returned by APIs.

To make our RestTemplate handle an error and throw a custom exception, we will need to implement the following:

  1. Create a custom exception that we want to throw,
  2. To enable RestTemplate to handle errors, we will implement ResponseErrorHandler interface,
  3. Finally, we will write a test method for our RestTemplate error handling implementation.

1. Create Custom Exceptions

We can do the custom implementation of as many exceptions as we want. We’ll keep it simple for now:

public class ServiceUnAvailableException  extends RuntimeException {
    
    public ServiceUnAvailableException() {
        super();
    }
    public ServiceUnAvailableException(String message) {
        super(message);
    }  
}
public class UnAuthorizedException  extends RuntimeException {
    
    public UnAuthorizedException() {
        super();
    }
    public UnAuthorizedException(String message) {
        super(message);
    }    
}

//more exceptions

2. ResponseErrorHandler Implementation

In case of an HTTP error, RestTemplate throws one of these exceptions:

  • HttpClientErrorException – when status is 4xx.
  • HttpServerErrorException – when status is  5xx.
  • UnknownHttpStatusCodeException – when the status is unknown.

Rather than handling the errors through a try/catch block, we’ll be implementing the ResponseErrorHandler interface. For the ResponseErrorHanlder interface, here, we’ll implement two methods:

  1. boolean hasError(ClientHttpResponse response) – In this method, we’ll check whether the error returned by the API call has a client error status or a server error status.
  2. void handleError(ClientHttpResponse response) – Now, based on the type of error, client or server, and the status code, we’ll return a custom response of as many exceptions as we want:
@Component
public class RestTemplateErrorHandler implements ResponseErrorHandler {
    
    

    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {

        return response.getStatusCode().is4xxClientError() || response.getStatusCode().is5xxServerError();

    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        if (response.getStatusCode().is5xxServerError()) {

            if (response.getStatusCode() == HttpStatus.SERVICE_UNAVAILABLE) {

                throw new ServiceUnAvailableException("Service is currently unavailable");

            }

            // handle more server errors

        } else if (response.getStatusCode().is4xxClientError()) {
            if (response.getStatusCode() == HttpStatus.UNAUTHORIZED) {

                throw new UnAuthorizedException("Unauthorized access");

            }

            // handle more client errors

        }

    }

}

 

3. Testing RestTemplate Error Handling

Let’s head to writing an integration test for ResponseErrorHandler interface implementation. Some important points to note:

  • Declare a MockRestServiceServer instance,
  • Declare a RestTemplate instance. You might also want to learn about Spring’s TestRestTemplate object.
  • To build the RestTemplate, declare a RestTemplateBuilder instance,
  • In beforeAllTests() method, inject the ResponseErrorHandler implementation into RestTemplate instance and then build it using the RestTemplateBuilder instance.
@ContextConfiguration(classes = { UnAuthorizedException.class, ServiceUnAvailableException.class })
@RestClientTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class RestTemplateErrorHandlerIntegrationTest {

    @Autowired
    MockRestServiceServer restServiceServer;
    @Autowired
    RestTemplateBuilder builder;
    RestTemplate restTemplate;
    String URL = "/students/1";

    @BeforeAll
    public void beforeAllTests() {
        Assertions.assertNotNull(this.builder);
        Assertions.assertNotNull(this.restServiceServer);
        restTemplate = this.builder.errorHandler(new RestTemplateErrorHandler()).build();

    }

//more code

}

void whenErrorIs401_thenThrowUnAuthorizedException()

In the first test:

  • We’ll mock a server that expects a request URL to delete a student and a delete method and will return a 401 status.
  • Now when we hit the server with the delete request, it returns the UnauthorizedException as expected.
  • This exception will be handled by the ResponseErrorHandler implementation.
  • Now the error returned will be the custom implementation of the UnauthorizedException.
  • After running the test, we see that the error returned is the same as expected.
@Test
public void whenErrorIs401_thenThrowUnAuthorizedException() {

    this.restServiceServer.expect(requestTo(URL)).andExpect(method(HttpMethod.DELETE))
            .andRespond(withUnauthorizedRequest());

    Assertions.assertThrows(UnAuthorizedException.class, () -> {
        restTemplate.delete(URL);

    });
}

void whenErrorIs503_thenThrowServiceUnAvailableException()

In the second test:

  • We’ll mock a server again, but the server will return a 503 status this time.
  • Now when we hit the server with the delete request, it returns the ServiceUnavailableException as expected.
  • This exception will also be handled by the ResponseErrorHandler implementation.
  • Now the error returned will be the custom implementation of the ServiceUnavailableException.
  • After running the test, we see that the error returned is the same as expected.
@Test
public void whenErrorIs503_thenThrowServiceUnAvailableException() {

    this.restServiceServer.expect(requestTo(URL)).andExpect(method(HttpMethod.DELETE))
            .andRespond(withStatus(HttpStatus.SERVICE_UNAVAILABLE));

    Assertions.assertThrows(ServiceUnAvailableException.class, () -> {
        restTemplate.delete(URL);

    });
}

Conclusion

With this, we have come to the end of our tutorial.

In this tutorial, we learned to implement RestTemplate Error Handling Example. Along with this, we also tested our implementation by writing an integration test.

The complete code for the tutorial can be found on GitHub.