Exception Handling in Spring Boot

Reading Time: 4 minutes

Exception Handling in Spring boot correctly in APIs while providing meaningful error messages is a very desirable feature, as it can help the API client properly respond to issues. The default behavior tends to be returning stack traces that are hard to understand and ultimately useless for the API client. Partitioning the error information into fields also enables the API client to parse it and provide better error messages to the user. In this article, we will cover how to do proper error handling when building a REST API with Spring Boot.

Spring Exception Handling

Spring MVC Framework provides the following ways to help us achieving robust exception handling.

  1. Controller Based – We can define exception handler methods in our controller classes. All we need is to annotate these methods with @ExceptionHandler annotation. This annotation takes Exception class as argument. So if we have defined one of these for Exception class, then all the exceptions thrown by our request handler method will have handled.These exception handler methods are just like other request handler methods and we can build error response and respond with a different error page. We can also send JSON error response, that we will look later on in our example.
  2. Global Exception Handler – Exception Handling is a cross-cutting concern, it should be done for all the pointcuts in our application. Spring provides @ControllerAdvice annotation that we can use with any class to define our global exception handler. The handler methods in Global Controller Advice is same as Controller based exception handler methods and used when controller class is not able to handle the exception.
  3. HandlerExceptionResolver – For generic exceptions, most of the times we serve static pages. Spring Framework provides HandlerExceptionResolver interface that we can implement to create global exception handler. The reason behind this additional way to define global exception handler is that Spring framework also provides default implementation classes that we can define in our spring bean configuration file to get spring framework exception handling benefits.SimpleMappingExceptionResolver is the default implementation class, it allows us to configure exceptionMappings where we can specify which resource to use for a particular exception. We can also override it to create our own global handler with our application specific changes, such as logging of exception messages.

@ExceptionHandler

An alternative to the HandlerExceptionResolver interface is the @ExceptionHandler annotation. You use the @ExceptionHandler method annotation within a controller to specify which method is invoked when an exception of a specific type is thrown during the execution of controller methods. For example:

 @ExceptionHandler({DogsNotFoundException.class})
    public ResponseEntity<String> handleNotFoundException(DogsNotFoundException e) {
        return error(NOT_FOUND, e);
    }

@ControllerAdvice

ControllerAdvice is an annotation introduced in Spring 3.2, and as the name suggests, is “Advice” for multiple controllers. It is used to enable a single ExceptionHandler to be applied to multiple controllers. This way we can in just one place define how to treat such an exception and this handler will be called when the exception is thrown from classes that are covered by this ControllerAdvice. The subset of controllers affected can defined by using the following selectors on @ControllerAdviceannotations()basePackageClasses(), and basePackages(). If no selectors are provided, then the ControllerAdvice is applied globally to all controllers.

So by using @ExceptionHandler and @ControllerAdvice, we’ll be able to define a central point for treating exceptions and wrapping them up in an ApiError object with better organization than the default Spring Boot error handling mechanism. for example:

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.NOT_FOUND;
@ControllerAdvice
@Slf4j
public class DogsServiceErrorAdvice {
    @ExceptionHandler({RuntimeException.class})
    public ResponseEntity<String> handleRunTimeException(RuntimeException e) {
        return error(INTERNAL_SERVER_ERROR, e);
    }
    @ExceptionHandler({DogsNotFoundException.class})
    public ResponseEntity<String> handleNotFoundException(DogsNotFoundException e) {
        return error(NOT_FOUND, e);
    }
    @ExceptionHandler({DogsServiceException.class})
    public ResponseEntity<String> handleDogsServiceException(DogsServiceException e){
        return error(INTERNAL_SERVER_ERROR, e);
    }
    private ResponseEntity<String> error(HttpStatus status, Exception e) {
        log.error("Exception : ", e);
        return ResponseEntity.status(status).body(e.getMessage());
    }
}

See what is happening here:

  • handleRunTimeException: This method handles all the RuntimeException and returns the status of INTERNAL_SERVER_ERROR.
  • handleNotFoundException: This method handles DogsNotFoundException and returns NOT_FOUND.
  • handleDogsServiceException: This method handles DogsServiceException and returns INTERNAL_SERVER_ERROR.

Customizing Exception Handling with Spring Boot

A combination of Spring and Spring Boot provides multiple options to customize responses for errors.

Customizing Return Status for a Specific Exception

You can specify the Response Status for a specific exception along with the definition of the Exception with ‘@ResponseStatus’ annotation.

@ResponseStatus(HttpStatus.NOT_FOUND)
public class StudentNotFoundException extends RuntimeException {

This is the response when you try getting details of a non existing student http://localhost:8080/students/9.

{
  "timestamp": 1512714594153,
  "status": 404,
  "error": "Not Found",
  "message": "id-9",
  "path": "/students/9"
}

Customizing Error Response Structure

Default error response provided by Spring Boot contains all the details that are typically needed.

However, you might want to create a framework independent response structure for your organization. In that case, you can define a specific error response structure.

Let’s define a simple error response bean

public class ErrorDetails {
  private Date timestamp;
  private String message;
  private String details;

  public ErrorDetails(Date timestamp, String message, String details) {
    super();
    this.timestamp = timestamp;
    this.message = message;
    this.details = details;
  }
@ControllerAdvice
@RestController
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

  @ExceptionHandler(StudentNotFoundException.class)
  public final ResponseEntity<ErrorDetails> handleUserNotFoundException(StudentNotFoundException ex, WebRequest request) {
    ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
        request.getDescription(false));
    return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
  }

Notes

  • @ExceptionHandler(StudentNotFoundException.class) indicates that this method would handle exceptions of the specific type.
  • new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND) – Create an error response object and return it with a specific Http Status.

This is the response when you try getting details of a non existing student http://localhost:8080/students/9

{
“timestamp”: 1512714887537,
“message”: “id-9”,
“details”: “uri=/students/9”
}

Response uses the custom error structure that we had defined earlier.

Conclusion

So, in this Spring Rest Service Exception Handling tutorial, we have seen how to handle an exception with a Spring web application. Spring’s exception abstraction frees you from writing those bulky catch blocks and improve the readability of your code with the help of annotations.

Reference

https://docs.spring.io/spring/docs/3.0.0.M4/spring-framework-reference/html/ch15s09.html

Written by 

Munander is a Software Consultant in Knoldus Software LLP. He has done b.tech from IMS Engineering college, Ghaziabad. He has decent knowledge of C,C++,Java,Angular and Lagom. He always tries to explore new technologies. His hobbies include playing cricket and adventure.