Here we are going describe how to implement Exception Handling with Spring for a REST API.
Before Spring 3.2
There are two main approaches to handling exceptions in a Spring MVC application were: HandlerExceptionResolver or the @ExceptionHandler annotation. Both of these have some clear downsides.
After Spring 3.2
We now have the new @ControllerAdvice annotation to address the limitations of the previous two solutions.
All of these do have one thing in common – they deal with the separation of concerns very well. The app can throw exception normally to indicate a failure of some kind – exceptions which will then be handled separately.
Here we will define a method to handle exceptions, and annotate that with @ExceptionHandler at the controller level. This solution is limited to the controller only for the same type of exceptions i.e. this approach has a major drawback – the @ExceptionHandler annotated method is only active for that particular Controller, not globally for the entire application. Of course, adding this to every controller makes it not well suited for a general exception handling mechanism.
We can avoid this limitation by making base controller which is extended by every controller in the application however this can be a problem for applications where, for whatever reasons, the Controllers cannot be made to extend from such a class because of this, not a good approach for reducing loose coupling.
@Controller public class WebController { @ExceptionHandler(StudentNotFoundException.class) public ModelAndView handleStudentNotFoundException(StudentNotFoundException ex) { Map<String, String> model = new HashMap<String, String>(); model.put("exception", ex.toString()); return new ModelAndView("student.error", model); } }
From Spring 3.2 offers to global exception handling @ExceptionHandler with the new @ControllerAdvice annotation, this enables a mechanism that breaks away from the older MVC model and makes use of ResponseEntity along with the type safety and flexibility of @ExceptionHandler:
package com.doj.spring.web.controller; import org.springframework.http.HttpHeaders; 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 org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @ControllerAdvice public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(value = { IllegalArgumentException.class, IllegalStateException.class }) protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest request) { String bodyOfResponse = "This should be application specific"; return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus.CONFLICT, request); } }
The new annotation allows the multiple scattered @ExceptionHandler from before to be consolidated into a single, global error handling component.
The actual mechanism is extremely simple but also very flexible:
One thing to keep in mind here is to match the exceptions declared with @ExceptionHandler with the exception used as argument of the method. If these don’t match, the compiler will not complain – no reason it should, and Spring will not complain either.
It will also allow us to implement a uniform exception handling mechanism in our REST API.
ExceptionHandlerExceptionResolver
This resolver was introduced in Spring 3.1 and is enabled by default in the DispatcherServlet. This is actually the core component of how the @ExceptionHandler mechanism presented earlier works.
DefaultHandlerExceptionResolver
This resolver was introduced in Spring 3.0 and is enabled by default in the DispatcherServlet. It is used to resolve standard Spring exceptions to their corresponding HTTP Status Codes.
ResponseStatusExceptionResolver
This resolver was also introduced in Spring 3.0 and is enabled by default in the DispatcherServlet. It’s main responsibility is to use the @ResponseStatus annotation available on custom exceptions and to map these exceptions to HTTP status codes.
package com.doj.spring.web.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Student Not Found") public class StudentNotFoundException extends RuntimeException{ /** * */ private static final long serialVersionUID = -2581975292273282583L; String errorMessage; String errorCode; public StudentNotFoundException(String errorMessage, String errorCode) { super(); this.errorMessage = errorMessage; this.errorCode = errorCode; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } }
Same as the DefaultHandlerExceptionResolver, this resolver is limited in the way it deals with the body of the response – it does map the Status Code on the response, but the body is still null.
Custom HandlerExceptionResolver
The combination of DefaultHandlerExceptionResolver and ResponseStatusExceptionResolver goes a long way towards providing a good error handling mechanism for a Spring RESTful Service. The downside is – as mentioned before – no control over the body of the response.
Ideally, we’d like to be able to output either JSON or XML, depending on what format the client has asked for via the Accept header.
MVC – Custom Error Page
XML configuration:
<http> <intercept-url pattern="/admin/*" access="hasAnyRole('ROLE_ADMIN')"/> ... <access-denied-handler error-page="/custom-error-page" /> </http>
Java Configuration:
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/*").hasAnyRole("ROLE_ADMIN") ... .and() .exceptionHandling().accessDeniedPage("/custom-error-page"); }
When users tries to access a resource without having enough authorities, they will be redirected to “/custom-error-page”.
This tutorial discussed several ways to implement an exception handling mechanism for a REST API in Spring.
Strategy Design Patterns We can easily create a strategy design pattern using lambda. To implement…
Decorator Pattern A decorator pattern allows a user to add new functionality to an existing…
Delegating pattern In software engineering, the delegation pattern is an object-oriented design pattern that allows…
Technology has emerged a lot in the last decade, and now we have artificial intelligence;…
Managing a database is becoming increasingly complex now due to the vast amount of data…
Overview In this article, we will explore Spring Scheduler how we could use it by…