Saturday, August 12, 2017

API Exception Handling

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.


2. The Controller level using @ExceptionHandler

Here we will define a method to handle exceptions, and annotate that with @ExceptionHandler at controller level. This solution is limited to the controller only for 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 controllers 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 this not good approach for reducing loose coupling.


  1. @Controller

  2. public class WebController {

  3.        @ExceptionHandler(StudentNotFoundException.class)

  4.  public ModelAndView handleStudentNotFoundException(StudentNotFoundException ex) {

  5.   Map<String, String> model = new HashMap<String, String>();

  6.   model.put("exception", ex.toString());

  7.   return new ModelAndView("student.error", model);



  8.  }

  9. }



3. The New @ControllerAdvice (Spring 3.2 and Above)

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:


  1. package com.doj.spring.web.controller;



  2. import org.springframework.http.HttpHeaders;

  3. import org.springframework.http.HttpStatus;

  4. import org.springframework.http.ResponseEntity;

  5. import org.springframework.web.bind.annotation.ControllerAdvice;

  6. import org.springframework.web.bind.annotation.ExceptionHandler;

  7. import org.springframework.web.context.request.WebRequest;

  8. import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;



  9. @ControllerAdvice

  10. public class RestResponseEntityExceptionHandler extends

  11.   ResponseEntityExceptionHandler {

  12.  @ExceptionHandler(value = { IllegalArgumentException.class, IllegalStateException.class })

  13.     protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest request) {

  14.         String bodyOfResponse = "This should be application specific";

  15.         return handleExceptionInternal(ex, bodyOfResponse,

  16.           new HttpHeaders(), HttpStatus.CONFLICT, request);

  17.     }

  18. }



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:

  • it allows full control over the body of the response as well as the status code
  • it allows mapping of several exceptions to the same method, to be handled together
  • it makes good use of the newer RESTful ResposeEntity response


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.

4. The HandlerExceptionResolver

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.


  1. package com.doj.spring.web.exception;



  2. import org.springframework.http.HttpStatus;

  3. import org.springframework.web.bind.annotation.ResponseStatus;



  4. @ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Student Not Found")

  5. public class StudentNotFoundException extends RuntimeException{



  6.  /**

  7.   *

  8.   */

  9.  private static final long serialVersionUID = -2581975292273282583L;

  10.  

  11.  String errorMessage;

  12.  

  13.  String errorCode;



  14.  public StudentNotFoundException(String errorMessage, String errorCode) {

  15.   super();

  16.   this.errorMessage = errorMessage;

  17.   this.errorCode = errorCode;

  18.  }



  19.  public String getErrorMessage() {

  20.   return errorMessage;

  21.  }



  22.  public void setErrorMessage(String errorMessage) {

  23.   this.errorMessage = errorMessage;

  24.  }



  25.  public String getErrorCode() {

  26.   return errorCode;

  27.  }



  28.  public void setErrorCode(String errorCode) {

  29.   this.errorCode = errorCode;

  30.  }

  31.  

  32. }



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.

5. Handle the Access Denied in Spring Security

MVC – Custom Error Page
XML configuration:



  1.     pattern="/admin/*" access="hasAnyRole('ROLE_ADMIN')"/>  

  2.     ...

  3.     error-page="/custom-error-page" />




Java Configuration:

  1. @Override

  2. protected void configure(HttpSecurity http) throws Exception {

  3.     http.authorizeRequests()

  4.         .antMatchers("/admin/*").hasAnyRole("ROLE_ADMIN")

  5.         ...

  6.         .and()

  7.         .exceptionHandling().accessDeniedPage("/custom-error-page");

  8. }



When users tries to access a resource without having enough authorities, they will be redirected to "/custom-error-page".


---------------------------------------------

You don't need to extend ResponseEntityExceptionHandler if you don't need all the exception mappings it provides.

A simpler way to write your exception handling:

@ControllerAdvice
public class RestResponseEntityExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    protected ResponseEntity handleResourceNotFound(ResourceNotFoundException ex){

      return ResponseEntity
              .status(HttpStatus.NOT_FOUND)
              .body("Requested resource does not found");
    }

    @ExceptionHandler(InvalidInputException.class)
    protected ResponseEntity handleInvalidInput(InvalidInputException ex){

      return ResponseEntity
              .badRequest()
              .body("Invalid Input");
    }
}

Note that the ResponseEntity builder API has been in introduced in Spring 4.1, but you can use the regular constructor on 4.0.x.

No comments:

Post a Comment

உப்பு மாங்காய்

சுருக்குப்பை கிழவி. சுருக்கங்கள் சூழ் கிழவி. பார்க்கும் போதெல்லாம் கூடையுடனே குடியிருப்பாள். கூடை நிறைய குட்டி குட்டி மாங்காய்கள். வெட்டிக்க...