Rest API Error Handling in Spring Boot Applications.

Samuel Mumo
5 min readApr 6, 2021

Introduction

This article assumes you are familiar with spring boot and you are advancing your knowledge of creating restful APIs.

Uniform error handling when creating REST APIs is very important to the consumers of our APIs. There are very many types of errors that can occur when consuming APIs. The common errors include validation errors, authentication and authorization errors, internal server errors e.t.c. Generally, we don’t want to display error exceptions to the users of our API but meaningful error messages. Different organizations may want to implement different ways of showing errors for their APIs.

In this article, we are going to look at a simple way of implementing error handling in spring-boot which can form a basis for implementing more complex implementations to your organization's needs using controller advice and exception handlers provided in spring. We are going to look at how to handle the following type of errors.

i) Custom application exceptions

ii) Validation errors

Our API will display general errors using the format below.

{
error: {
"statusCode": "404",
"message": "Product with Id `90` was not found"
}
}

For validation errors, we need to show more information as shown below.

{
error: {
"status": "400",
"message": "Validation errors",
"errors": [
{
"fieldName": "email",
"value": "jan@email,
"message": "invalid email"
},
{
"fieldName": "age",
"value": "sixty years",
"message": "age must be a number"
}
]
}
}

i) Custom Exception handling.

In this case, we are throwing custom exceptions in our application. For example, we have an API that provides product information when given an id. In case the product is not found in the database, we throw an exception as shown below.

code snippet for fetching product information by id.

Below is the implementation of ResourceNotFoundException class.

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

To handle the above error and return the response in the JSON format we described above, we will first create a model that models the error. Let's call the model class ApiError as shown below.

we are using Lombok annotations to avoid boilerplate code for getters and setters. Now that we have our JSON error representation modeled as a java class, let's implement a class that uses controller advice to create the response in the format above. Let’s call the class RestApiHandler.java

Some interesting things are happening in this class.

First, we are marking it with @ControllerAdvice annotation. A controller advice allows you to use exactly the same exception handling techniques but apply them across the whole application, not just to an individual controller. You can think of them as an annotation-driven interceptor. This advice will handle all errors across the application.

Second, we are using @Order(Ordered.HIGHEST_PRECEDENCE) to tell spring to give this class the highest priority amongst other components.

Thirdly, we have a method ‘resourceNotFoundHandler’ that accepts an exception and return an ApiResponse object. we are going to look at the details of ApiResponse and ApiResponseUtil classes shortly. But first, let's discuss the roles of various annotations in the method.

  • @ExceptionHandler(ResourceNotFoundException.class) — is an annotation used to handle the specific exceptions and sending the custom responses to the client. In this case, we are handling ResourceNotFoundException
  • @ResponseBody — is a Spring annotation which binds a method return value to the web response body. It uses HTTP Message converters to convert the return value to the HTTP response body, based on the content-type in the request HTTP header.
  • @ResponseStatus(HttpStatus.NOT_FOUND) — is an annotation that adds HTTP Status Not found (404) in the response.

Now that we have an understanding of what is happening in the method. Let’s look at ApiResponse class. It is simply a POJO class that models the response we send to back.

The class models the overall response that we send back. It also has @JsonInclude(JsonInclude.Include.NON_NULL) which is jackson annotation that ignores null values when serializing java objects. i.e create JSON from a java class. That means, if we don’t pass the ApiError, the error field won’t be included in the ApiResponse, in the case that we don’t have errors.

The ApiResponseUtil class simply has static helper methods for creating an ApiResponse object. Below is the implementation.

Now that you have a general understanding of how to handle custom exceptions, let’s explain how to handle authorization and authentication errors to reinforce the understanding.

ii) Validation error handling.

This section describes how to handle validation errors when using bean validation in spring-boot. Bean validation works by defining constraints using certain annotations. Then, you pass an object of that class into a Validator which checks if the constraints are satisfied. Below is example

As shown above, we have a bean `Input` class that is annotated with constraint annotations from hibernate validator. We then pass this to spring controller class in validateInput method. The method parameter has 2 annotations. The @RequestBody annotation tells spring to read and map the body of the request to the Input class. The @Valid annotation tells spring to pass the input to Validator before anything else.

If the validation fails, it will trigger a MethodArgumentNotValidException. By default, Spring will translate this exception to an HTTP status 400 (Bad Request). We want to return a custom error response as shown below.

{
error: {
"status": "400",
"message": "Validation errors",
"errors": [
{
"fieldName": "email",
"value": "jan@email,
"message": "invalid email"
},
{
"fieldName": "age",
"value": "sixty years",
"message": "age must be a number"
}
]
}
}

To achieve this, we need to create a class to model the validation error as below.

Then, let's modify our ApiError class we defined earlier.

The class includes errors variable which is a list of Validation Error we created. we also have an additional helper method for adding a validation error. Finally, let's define an exception handler method in our RestExceptionHandler class we defined earlier.

The method has a similar signature as the method we defined for handling custom exceptions. The only difference is how we are creating our ApiError object which is straightforward. We create the ApiError object by passing the status code and message. Then loop over the field errors from the exception class and add them to our apiError object using the helper method we defined earlier.

Now, if we encounter any field validation error, we will have a nice response which is what we wanted other than the default api response returned by spring.

Hope this article has given you a basic understanding of how to customize Rest API error responses in spring-boot by creating global error handlers for your application using @ControllerAdvice and @ExceptionHandler annotations. There are several improvements that can be done however hope this has given you an idea of how to implement uniform API error responses across the application.

Thanks for reading. Cheers

--

--