Resttemplate error handler

Below is the code snippet; basically, I am trying to propagate the exception when the error code is anything other than 200. ResponseEntity response = restTemplate.exchange(url.toStr...

The code of exchange is below:

public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
            HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException

Exception RestClientException has HttpClientErrorException and HttpStatusCodeException exception.

So in RestTemplete there may occure HttpClientErrorException and HttpStatusCodeException exception.
In exception object you can get exact error message using this way: exception.getResponseBodyAsString()

Here is the example code:

public Object callToRestService(HttpMethod httpMethod, String url, Object requestObject, Class<?> responseObject) {

        printLog( "Url : " + url);
        printLog( "callToRestService Request : " + new GsonBuilder().setPrettyPrinting().create().toJson(requestObject));

        try {

            RestTemplate restTemplate = new RestTemplate();
            restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
            restTemplate.getMessageConverters().add(new StringHttpMessageConverter());


            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);

            long start = System.currentTimeMillis();

            ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);

            printLog( "callToRestService Status : " + responseEntity.getStatusCodeValue());


            printLog( "callToRestService Body : " + new GsonBuilder().setPrettyPrinting().create().toJson(responseEntity.getBody()));

            long elapsedTime = System.currentTimeMillis() - start;
            printLog( "callToRestService Execution time: " + elapsedTime + " Milliseconds)");

            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                return responseEntity.getBody();
            }

        } catch (HttpClientErrorException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }catch (HttpStatusCodeException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }
        return null;
    }

Here is the code description:

In this method you have to pass request and response class. This method will automatically parse response as requested object.

First of All you have to add message converter.

restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
            restTemplate.getMessageConverters().add(new StringHttpMessageConverter());

Then you have to add requestHeader.
Here is the code:

HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);

Finally, you have to call exchange method:

ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);

For prety printing i used Gson library.
here is the gradle : compile 'com.google.code.gson:gson:2.4'

You can just call the bellow code to get response:

ResponseObject response=new RestExample().callToRestService(HttpMethod.POST,"URL_HERE",new RequestObject(),ResponseObject.class);

Here is the full working code:

import com.google.gson.GsonBuilder;
import org.springframework.http.*;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;


public class RestExample {

    public RestExample() {

    }

    public Object callToRestService(HttpMethod httpMethod, String url, Object requestObject, Class<?> responseObject) {

        printLog( "Url : " + url);
        printLog( "callToRestService Request : " + new GsonBuilder().setPrettyPrinting().create().toJson(requestObject));

        try {

            RestTemplate restTemplate = new RestTemplate();
            restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
            restTemplate.getMessageConverters().add(new StringHttpMessageConverter());


            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);

            long start = System.currentTimeMillis();

            ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);

            printLog( "callToRestService Status : " + responseEntity.getStatusCodeValue());


            printLog( "callToRestService Body : " + new GsonBuilder().setPrettyPrinting().create().toJson(responseEntity.getBody()));

            long elapsedTime = System.currentTimeMillis() - start;
            printLog( "callToRestService Execution time: " + elapsedTime + " Milliseconds)");

            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                return responseEntity.getBody();
            }

        } catch (HttpClientErrorException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }catch (HttpStatusCodeException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }
        return null;
    }

    private void printLog(String message){
        System.out.println(message);
    }
}

Thanks :)

RestTemplate throws RestClientResponseException subtypes such as HttpClientErrorException, HttpServerErrorException and UnknownHttpStatusCodeException separately if the response HTTP status code is 4xx, 5xx and unknown

You can handle RestTemplate errors at the local level by catching the RestClientResponseException, at the bean level by implementing the ResponseErrorHandler interface and plugging into a RestTemplate bean

Let’s walk through this tutorial to explore them in more detail examples

The RestClientResponseException

RestClientResponseException is a common base class for exceptions that contain actual HTTP response data

You can use getRawStatusCode, getStatusText, getResponseHeaders, getResponseBodyAsString to get HTTP status code in integer number, get HTTP response headers, and get HTTP response body as a String. They are useful when you need to log and return detail error to the client

Catch RestClientResponseException

RestTemplate can only return a ResponseEntity with HTTP 2xx status. With 4xx and 5xx status, RestTemplate throws HttpClientErrorException and HttpServerErrorException which are extended from HttpStatusCodeException and RestClientResponseException hierarchically

<T> ResponseEntity consumeWebService(String url, Class<T> responseType) {  
    try {
        return restTemplate.getForEntity(url, responseType);
    } catch (RestClientResponseException e) {
        return ResponseEntity
            .status(e.getRawStatusCode())
            .body(e.getResponseBodyAsString());
    }
}

Beside catching RestClientResponseException, you can also catch HttpStatusCodeException

The following gives you an integration test example with MockRestServiceServer


Implement ResponseErrorHandler

If you don’t like try-catch every time executing a RestTemplate request, try to implement ResponseErrorHandler and add it to the RestTemplate bean


hasError method is used to indicate whether the given response has any errors

The two handleError methods are used to handle the error in the given response. Only handleError(response) is required to implement, however, it will be overshadowed if you implement handleError(URI url, HttpMethod method, ClientHttpResponse response)

Plug your custom ResponseErrorHandler into a RestTemplate bean

To make your custom ResponseErrorHandler work, you have to register it with a RestTemplate bean

In practice, to follow DRY and leverage the Spring Dependency Injection, you can define your RestTemplate beans in a separate class config and inject it via @Autowired into where it is used such as Spring @Component or @Service class



The following gives you an integration test example with SpringRunner and MockRestServiceServer


Conclusion

In this tutorial, we learned to handle RestTemplate error by catching RestClientResponseException and implementing ResponseErrorHandler. You can find the full source code on GitHub

Share to social

Giau Ngo

Giau Ngo is a software engineer, creator of HelloKoding. He loves coding, blogging, and traveling. You may find him on GitHub and LinkedIn

Handling exceptions coming from your RestTemplate instances is important because they are subclasses of RuntimeException so if you don’t catch them they will be thrown up to your top layer (let’s say a @RestController layer).

To handle those exceptions you can catch them in a @ControllerAdvice error handler.

Here is a way to handle RestTemplate exceptions, in this example the application have multiple RestTemplate instances that calls different APIs.

List the APIs you call in an enum

First let’s define an enum that will list all the downstream APIs you will call using RestTemplate.

This enumeration will be used for logging purposes, so that when you catch an exception you can log (or do whatever you want) that an error happened when calling that downstream API.

public enum DownstreamApi {
  MY_API_1,
  MY_API_2
  // TODO list all the downstream APIs here
}

Custom RestTemplate error handling

When using a RestTemplate, the default error handling will throw an exception when the call returned a HTTP 4xx or HTTP 5xx.

The goal here is to convert each HTTP 4xx and HTTP 5xx to a custom RuntimeException.

This custom exception will hold information about the downstream API, the HTTP response status and an error message.

public class MyRestTemplateException extends RuntimeException {

  private DownstreamApi api;
  private HttpStatus statusCode;
  private String error;

  public MyRestTemplateException(DownstreamApi api, HttpStatus statusCode, String error) {
    super(error);
    this.api = api;
    this.statusCode = statusCode;
    this.error = error;
  }
  
  // TODO getters ... 
  // TODO toString ...
}

Here is the code that will catch every HTTP 4xx and 5xx errors thrown by RestTemplate when calling the API “MY_API1”.

You should create a custom error handler per API so you can handle them in a different way (for example if they have different error responses you can parse it here and provide more information in your “global” exception MyRestTemplateException.java).

public class MyApiRestTemplateErrorHandler extends DefaultResponseErrorHandler {

  @Override
  public void handleError(ClientHttpResponse response) throws IOException {
    if (response.getStatusCode().is4xxClientError() || response.getStatusCode().is5xxServerError()) {
      try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.getBody()))) {
        String httpBodyResponse = reader.lines().collect(Collectors.joining(""));

        // TODO deserialize (could be JSON, XML, whatever...) httpBodyResponse to a POJO that matches the error structure for that specific API, then extract the error message.
        // Here the whole response will be treated as the error message, you probably don't want that.
        String errorMessage = httpBodyResponse;

        throw new MyRestTemplateException(DownstreamApi.MY_API_1, response.getStatusCode(), errorMessage);
      }
    }
  }
}

DAO

The DAO will make the REST call by using RestTemplate. The RestTemplate instance is created in a way it is using the custom RestTemplate Error Handler we defined earlier.

@Component
public class MyDao {

  // This is the RestTemplate for DownstreamApi.MY_API_1
  private RestTemplate restTemplateApi1;

  public MyDao(RestTemplateBuilder restTemplateBuilder) {
    this.restTemplateApi1 = restTemplateBuilder
        .errorHandler(new MyApiRestTemplateErrorHandler())
        .build();
  }
  
  public void updateStuff(String param1) {
    URI uri = UriComponentsBuilder
        .fromUriString("https://downstream-api.com/stuff/{param}")
        .build(param1);

    RequestEntity<Void> requestEntity = RequestEntity
        .post(uri)
        .build();

    restTemplateApi1.exchange(requestEntity, Object.class);
  }
  
  // TODO other API calls
  
}

Controller advice

Once you have converted the RestTemplate exceptions to MyRestTemplateException, you have to catch them in a global Exception Handler and convert it to a “generic” error response (here ErrorResponse.java)

Here is the “generic” error response POJO:

public class ErrorResponse {

  private String timestamp;

  /** HTTP Status Code */
  private int status;

  /** HTTP Reason phrase */
  private String error;

  /** A message that describe the error thrown when calling the downstream API */
  private String message;

  /** Downstream API name that has been called by this application */
  private DownstreamApi api;

  /** URI that has been called */
  private String path;

  public ErrorResponse(MyRestTemplateException ex, String path) {
    this.timestamp = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now());
    this.status = ex.getStatusCode().value();
    this.error = ex.getStatusCode().getReasonPhrase();
    this.message = ex.getError();
    this.api = ex.getApi();
    this.path = path;
  }

  // TODO getters ... 
  // TODO toString ...
}

Then defined the error handler in a @ControllerAdvice class and you are good to go!

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {

  private static final Logger LOGGER = LoggerFactory.getLogger(MyExceptionHandler.class);

  @ExceptionHandler(value = MyRestTemplateException.class)
  ResponseEntity<ErrorResponse> handleMyRestTemplateException(MyRestTemplateException ex, HttpServletRequest request) {
    LOGGER.error("An error happened while calling {} Downstream API: {}", ex.getApi(), ex.toString());
    return new ResponseEntity<>(new ErrorResponse(ex, request.getRequestURI()), ex.getStatusCode());
  }
}

Controller layer

@RestController
public class MyController {

  private final MyDao dao;

  MyController(MyDao dao){
    this.dao = dao;
  }

  @PostMapping("/update-stuff")
  public void updateStuff() {
    return dao.updateStuff("blabla");
  }

Sample of error returned

Here is the error response that would be returned when calling POST /update-stuff if the downstream API “MY_API1” throws an error:

{
  "timestamp": "2019-06-07T19:51:30.907",
  "status": 404,
  "error": "Not Found",
  "message": "The stuff 'blabla' has not been found",
  "api": "MY_API_1",
  "path": "/update-stuff"
}

Tested with Spring Boot 2.1.5.RELEASE

Содержание

  1. Spring Boot RestTemplate Error Handling
  2. Default error handling
  3. Error handling using try. catch
  4. Implementing a custom error handler
  5. Spring resttemplate handle exception
  6. 2.1. REST operations
  7. 2.1.1. Asynchronous operations
  8. 2.1.2. Asynchronous operations using the Task Parallel Library (TPL)
  9. 2.2. Configuring the RestTemplate
  10. 2.2.1. Base address
  11. 2.2.2. HTTP message converters
  12. 2.2.3. Error handling
  13. 2.2.4. Request factory
  14. 2.2.4.1. Silverlight support
  15. 2.2.5. Request interceptors
  16. spring-rest-template-error-handling
  17. Spring RestTemplate Error Handling
  18. 1. Overview
  19. 2. Default Error Handling
  20. 3. Implementing a ResponseErrorHandler
  21. 4. Testing our Implementation
  22. 5. Conclusion
  23. Error Handling for REST with Spring
  24. Introduction
  25. 1. Restful API Error / Exception Design
  26. 1.1 Default Exception Message
  27. 2. Spring REST Error Handling
  28. 2.1. Rest API Error Handling Class.
  29. 2.2. ExceptionHandler Annotation
  30. 3. The @ControllerAdvice Annotation
  31. 3.1. ResponseEntityExceptionHandler
  32. 3.2. MethodArgumentTypeMismatchException
  33. 3.3. HttpMessageNotReadable
  34. 4. Handling Custom Exceptions
  35. 5. Default Exception Handler
  36. 6. Spring Boot REST Error Handling
  37. 7. JSR 303 Validation Error (REST API)
  38. Summary

Spring Boot RestTemplate Error Handling

In an earlier article, I wrote about making HTTP requests using the RestTemplate class in a Spring Boot application.

In this short article, you’ll learn how to handle the errors thrown by the RestTemplate, during the execution of an HTTP request.

Default error handling

By default, if there is an error during the execution of the request or the server returns a non-successful HTTP status code (4xx or 5xx), RestTemplate will throw one of the following exceptions:

  • HttpClientErrorException — For HTTP status code 4xx
  • HttpServerErrorException — For HTTP status code 5xx
  • UnknownHttpStatusCodeException — In case of an unknown HTTP status code

All these exceptions extend a base class called RestClientResponseException that contains actual HTTP response data.

Error handling using try. catch

The simplest way to add a custom error handler is to use a try-catch block to catch the HttpStatusCodeException exception. From the HttpStatusCodeException instance, you can then get the response status code, body, and headers, as shown below:

Implementing a custom error handler

Sometimes, a try-catch block is not enough to handle errors as it is not scalable when the number of HTTP requests increases.

You may want to create a reusable custom error handler by implementing the ResponseErrorHandler interface as follows:

You can now create an instance of MyErrorHandler and pass it to the RestTemplate class:

Check out the Making HTTP Requests using RestTemplate in Spring Boot guide for more RestTemplate examples.

✌️ Like this article? Follow me on Twitter and LinkedIn. You can also subscribe to RSS Feed.

Источник

Spring resttemplate handle exception

Invoking RESTful services in .NET is typically done using the HttpWebRequest class. For common REST operations this approach is too low level as shown below.

There is also another class in the .NET framework, WebClient , that might also be considered for this long-hand approach but its lack of support for HTTP headers and HTTP status codes/descriptions generally makes it unsuitable for use in these kinds of broader HTTP interactions.

RestTemplate provides higher level methods that correspond to each of the six main HTTP methods that make invoking many RESTful services a one-liner and enforce REST best practices.

2.1. REST operations

Note
HTTP Method RestTemplate Method
DELETE Delete(Uri url) and 2 more
GET GetForObject (Uri url) and 2 more
GetForMessage (Uri url) and 2 more
HEAD HeadForHeaders(Uri url) and 2 more
OPTIONS OptionsForAllow(Uri url) and 2 more
POST PostForLocation (Uri url, object request) and 2 more
PostForObject (Uri url, object request) and 2 more
PostForMessage (Uri url, object request) and 2 more
PostForMessage(Uri url, object request) and 2 more
PUT Put(Uri url, object request) and 2 more

The two other methods mentioned take URI template arguments in two forms, either as a object variable length argument or an IDictionary .
For example,

The names of RestTemplate methods follow a naming convention, the first part indicates what HTTP method is being invoked and the second part indicates what is returned. For example,

The method GetForObject () will perform a GET, and return the HTTP response body converted into an object type of your choice.

The method PostForLocation() will do a POST, converting the given object into a HTTP request and return the response HTTP Location header where the newly created object can be found.

The method PostForMessage () will do a POST, converting the given object into a HTTP request and return the full HTTP response message composed of the status code and description, the response headers and the response body converted into an object type of your choice.

The request object to be POSTed or PUTed, may be a HttpEntity instance in order to add additional HTTP headers. An example is shown below.

These operations are synchronous and thus are not available for Silverlight and Windows Phone because all related network calls have to be asynchronous.

2.1.1. Asynchronous operations

All REST operations may also be invoked asynchronously as well.
The asynchronous methods are suffixed by the word ‘Async’ based on common .NET naming conventions.

Although asynchronous call were designed to support Silverlight and Windows Phone, asynchronous invocation can also be used for Windows Forms and WPF applications, etc. to avoid blocking the UI when invoking RESTful services.
An example using an asynchronous method is shown below :

2.1.2. Asynchronous operations using the Task Parallel Library (TPL)

All REST operations supports also the new Task-based Asynchronous Pattern (TAP) for asynchronous calls.
This is based on the new Task Parallel Library (TPL) provided with .NET 4 and Silverlight 5.
These asynchronous methods are suffixed by the word ‘Async’ based on common .NET naming conventions, and each returns a Task .
An example is shown below :

Note

Do not forget to synchronize with the UI thread to interact with the user interface in a continuation as shown below :

Warning

Using the new TPL is the recommended way to perform asynchronous operations in .NET 4.0 and later.

2.2. Configuring the RestTemplate

2.2.1. Base address

In some cases it may be useful to set up the base url of the request once. This is possible by setting the base address in the constructor or by setting the property BaseAddress .
For example:

2.2.2. HTTP message converters

Objects passed to and returned from REST operations are converted to and from HTTP messages by IHttpMessageConverter instances.

Converters for the main mime types are registered by default, but you can also write your own converter and register it via the MessageConverters property.

The default converter instances registered with the template, depending of the target Framework, are ByteArrayHttpMessageConverter , StringHttpMessageConverter , FormHttpMessageConverter , XmlDocumentHttpMessageConverter , XElementHttpMessageConverter , Atom10FeedHttpMessageConverter and Rss20FeedHttpMessageConverter .

You can override these defaults using the MessageConverters property. This is required if you want to use the XmlSerializableHttpMessageConverter / DataContractHttpMessageConverter or DataContractJsonHttpMessageConverter / NJsonHttpMessageConverter .

See HTTP message conversion chapter for detailed description of each converter.

2.2.3. Error handling

In case of an exception processing the HTTP method, an exception of the type RestClientException will be thrown. The interface IResponseErrorHandler allows you to determine whether a particular response has an error and to handle it. The default implementation DefaultResponseErrorHandler throws an exception when a HTTP client or server error happens (HTTP status code 4xx or 5xx).

The default behavior can be changed by plugging in another implementation into the RestTemplate via the ErrorHandler property. The example below shows a custom IResponseErrorHandler implementation that inherits from existing DefaultResponseErrorHandler and which throws an exception only when a HTTP server error happens (HTTP status code 5xx).

Note

ErrorHandler property may be set to null to give the user total control over the response.

2.2.4. Request factory

RestTemplate uses a request factory to create instances of the IClientHttpRequest interface. Default implementation uses the .NET Framework class HttpWebRequest . This can be overridden by specifying an implementation of IClientHttpRequestFactory via the RequestFactory property.

The default implementation WebClientHttpRequestFactory uses an instance of HttpWebRequest which can be configured with credentials information or proxy settings. An example setting the proxy is shown below :

2.2.4.1. Silverlight support

In Silverlight, HTTP handling can be performed by the browser or the client. See How to: Specify Browser or Client HTTP Handling on MSDN.
By default, WebClientHttpRequestFactory will use the browser HTTP stack for HTTP methods GET and POST, and force the client HTTP stack for other HTTP methods.
This can be overridden by setting the WebRequestCreator property.

2.2.5. Request interceptors

RestTemplate allows you to intercept HTTP request creation and/or execution. You can create your own interceptor and register it via the RequestInterceptors property.

Four types of interceptors are provided :

IClientHttpRequestFactoryInterceptor will intercept request creation, allowing to modify the HTTP URI and method, and to customize the newly created request.

IClientHttpRequestBeforeInterceptor will intercept request before its execution, allowing to modify the HTTP headers and body. This interceptor supports both synchronous and asynchronous requests.

IClientHttpRequestSyncInterceptor will intercept synchronous request execution, giving total control of the execution (error management, logging, perf, etc.).

IClientHttpRequestAsyncInterceptor will intercept asynchronous request execution, giving total control of the execution (error management, logging, perf, etc.).

An example of an interceptor measuring HTTP request execution time is shown below.

Note that you can also make PerfRequestSyncInterceptor implement IClientHttpRequestAsyncInterceptor to support both synchronous and asynchronous requests.

Источник

spring-rest-template-error-handling

Spring RestTemplate Error Handling

1. Overview

In this short tutorial, we’ll discuss how to implement and inject the ResponseErrorHandler interface in a RestTemplate instance – to gracefully handle HTTP errors returned by remote APIs.

2. Default Error Handling

By default, the RestTemplate will throw one of these exceptions in case of an HTTP error:

HttpClientErrorException – in case of HTTP status 4xx

HttpServerErrorException – in case of HTTP status 5xx

UnknownHttpStatusCodeException – in case of an unknown HTTP status

All these exceptions are extensions of RestClientResponseException.

Obviously, the simplest strategy to add a custom error handling is to wrap the call in a try/catch block. Then, we process the caught exception as we see fit.

However, this simple strategy doesn’t scale well as the number of remote APIs or calls increases. It’d be more efficient if we could implement a reusable error handler for all of our remote calls.

3. Implementing a ResponseErrorHandler

And so, a class that implements ResponseErrorHandler will read the HTTP status from the response and either:

Throw an exception that is meaningful to our application

Simply ignore the HTTP status and let the response flow continue without interruption

We need to inject the ResponseErrorHandler implementation into the RestTemplate instance.

Hence, we use the RestTemplateBuilder to build the template and replace the DefaultResponseErrorHandler in the response flow.

So let’s first implement our RestTemplateResponseErrorHandler:

Next, we build the RestTemplate instance using the RestTemplateBuilder to introduce our RestTemplateResponseErrorHandler:

4. Testing our Implementation

Finally, let’s test this handler by mocking a server and returning a NOT_FOUND status:

5. Conclusion

This article presented a solution to implement and test a custom error handler for a RestTemplate that converts HTTP errors into meaningful exceptions.

As always, the code presented in this article is available over on Github.

Источник

Error Handling for REST with Spring

In the earlier post of REST with Spring series, we discussed Spring MVC Content Negotiation.In this article, we will discuss how to implement Spring REST Exception Handling.

Introduction

Exception handling in a RESTful API with a meaningful error message and the status code is a desired and must have feature. A good error message helps API client to take corrective actions. Some application sends the exception stack trace which can be a good option for a typical web application however this is not a very good solution for a REST API. In this post, we will discuss and implement Error Handling with Spring for a REST API.

In this article, we are using Spring Boot to develop and work on our REST API. Spring Boot provides several features which makes it easy and flexible to create REST API’s with minimum configurations. We will look at few methods outlining how to do a better error handling for REST API using Spring Boot.

1. Restful API Error / Exception Design

While designing exception handling in the RESTful API, it’s a good practice to set HTTP status code in the response to communicate why the request failed or showing a success. We should send more information along with HTTP status code. It will help the client understand the error and take any corrective action.

Let’s discuss this response to understand important points while designing response for your REST API.

  • The status represents HTTP status code.
  • error_code represents REST API specific error code.This field is helpful to pass on API / domain specific information.
  • The message field represents human-readable error message.
  • The details section represents error information with complete detail.
  • The information_link field specifies a link for detail information about the error or exception.

I am dividing this article in to 2 section. The first half talks about the importance of a better error handling for the REST API and the second half focused on the Spring Boot built-in feature for REST Exception Handling. We are taking a simple example of an online ecommerce application where our client can create a customer or get customer information by passing the customer id in the request.

1.1 Default Exception Message

Let’s see how the response look like in case REST API do not have a clear response.

This is the default output when there is no custom error response for our REST API. Though it provides a lot of information but it’s difficult for the client API to parse every exception and display the error message to the customer. There should be a better way to communicate these exceptions to the client to show a better error message.

2. Spring REST Error Handling

Spring and Spring Boot provides several options for error/exception handling. Let’s see what are the different options for Error Handling in REST API with Spring.Before we move in to the details, let’s create an Error response class for our Rest exception handling example.

2.1. Rest API Error Handling Class.

We will use this class to rest customer error message from our REST Api.

2.2. ExceptionHandler Annotation

The first approach is to use the ExceptionHandler annotation at the controller level. This annotation specifically handle exceptions thrown by request handling ( @RequestMapping ) methods in the same controller. Let’s take an example where service can throw CustomerNotFound Exception but we like to send a different / customize message to the client API.

We can define as many @RequestMapping in our controller (Having a different mapping for a different exception type).There are multiple problems or drawbacks with the approach.

  • This annotation is only active for the controller.
  • This annotation is not global and we need to add to every controller (not very intuitive).
  • The return type for this is void or String which add a lot of constraints.

Most of the enterprise application work by extending a basic controller (having common controller functionalities). We can overcome @ExceptionHandler limitation by adding it to the base controller. This also has multiple limitations.

  • The base controller is not suitable for all type of controller. We will end up by duplicating out code.
  • Our Controllers may have to extend a third party class not under our control.

[pullquote align=”normal”]With all these limitations, I do not recommend it to use this approach while building your RESTful API [/pullquote]

3. The @ControllerAdvice Annotation

Spring 3.2 introduced @ControllerAdvice annotation which supports global Exception handler mechanism. A controller advice allows you to use exactly the same exception handling techniques but applies them across the application, not just to an individual controller.

If we call our customer API endpoint again with an invalid user id, we will get the following response from the API

Let’s discuss some important points for the @ControllerAdvice annotation:

  1. The @ControllerAdvice annotation is a specialized @Component annotation. We have the flexibility to use this annotation for multiple controller classes (This works based on the Exception and not bind to the Controller).
  2. Spring Boot automatically detects all classes annotated with @ControllerAdvice during startup as part of the classpath scanning.
  3. We can narrow down the selected controllers by using basePackageClasses() , and basePackages() parameters. For more details refer to the ControllerAdvice.

3.1. ResponseEntityExceptionHandler

In the above example we extended ResponseEntityExceptionHandler class. This is a convenient base class for @ControllerAdvice classes that wish to provide centralized exception handling across all @RequestMapping methods through @ExceptionHandler methods. This is a convenient way while working on Spring based REST API since it allows the developer to specify ResponseEntity as return values. Let’s work on some most common client errors. We will look into few scenarios of a client sending an invalid request.

3.2. MethodArgumentTypeMismatchException

It throws this exception when method arguments are not the expected type

3.3. HttpMessageNotReadable

It throws this exception when API cannot read the HTTP message

Below we can see the answer to a REST call

4. Handling Custom Exceptions

While working on REST API, we may come across multiple use cases when a request is not fulfilled and we want to return a custom exception back to the client API. Let’s take a simple use case when client API call to find a customer by its unique id. Our service call might return a null or empty object if we do not find the object. Here, if not handled correctly, the API will return 200 (OK) response to the client even if no record found. Let’s create a simple example for better clarity:

Here is the sample service file.

In above example, if we send a request with the user id as 1, our REST API send 200 (OK) response.

To handle all similar use cases, we create a custom exception and handle this exception in our GlobalRestExceptionHandler

I will not go into details about handling different Exceptions in the REST API since we can handle all Exceptions in a similar way as explained above. Here is the list of some common exceptions in a REST API.

  • HttpMediaTypeNotSupportedException
  • HttpRequestMethodNotSupportedException
  • TypeMismatchException

5. Default Exception Handler

We can not handle each exception within the system. Let’s create a fallback handler which will handle all exceptions that don’t have specific exception handler.

6. Spring Boot REST Error Handling

Spring Boot provides several features to build RESTful API’s. Spring Boot 1.4 introduced the @RestControllerAdvice annotation for easier exception handling. It is a convenience annotation that is itself annotated with @ControllerAdvice and @ResponseBody. Here is an example.

While using above approach, set following property to true in Spring Boot application.properties file

7. JSR 303 Validation Error (REST API)

The JSR 303 or also known as bean validation is a standard way to validate your incoming data. The @valid annotation throws handleMethodArgumentNotValid error if the incoming data is not valid. In case we like to provide a custom error message, we have the same option to add a separate handler in our GlobalRestExceptionHandler class. This is our simple CustomerController :

We have added bean validation constraints to our Customer class:

Here is the custom spring rest exception handling method in our GlobalRestExceptionHandler class.

When we call our REST controller, we will have a custom error response based on the JSR 303 bean validation.

Summary

It is important to handle and process exceptions properly in the Spring bases REST API. In this post, we covered different options to implement Spring REST Exception Handling. Building a good exception handling workflow for REST API is an iterative and complex process. A good exception handling mechanism allows API client to know what went wrong with the request. The source code is available on the GitHub.

Источник

Adblock
detector

Note

Понравилась статья? Поделить с друзьями:
  • Restricted mode has hidden comments for this video как исправить
  • Restricted card error
  • Restricted area как изменить разрешение экрана
  • Restore job failed error failed to open vddk disk
  • Restore job failed error cannot complete file creation operation