Some time ago, I wrote about how the error response of a Spring based REST API can be enhanced, in order to provide the clients with a better understanding of why a request error occurred. In the first post I explained how a server-side exception triggered by an incoming request can be translated into a generic ErrorMessage class, which in turn was serialized to the response body. In the second part, the solution was generalized, to prevent the @ExceptionHandler methods from being duplicated to all controllers. However, the release of Spring 3.2 last December provides a new feature which greatly simplifies the general solution.

@ControllerAdvice

The @ControllerAdvice is a new TYPE annotation that was added as part of the release. A class annotated with it will act as a global helper class for all controllers. In other words, any local, controller specific @ExceptionHandler method that is moved from the @Controller class to a class annotated with @ControllerAdvice, will be applicable for the entire application. Consequently, all the boiler plate code that was created in the generic solution in my last post can be removed. The revisited solution can be as simple as:

The previous posts contained error handling examples of some Spring MVC exceptions, namely the MethodArgumentNotValidException, the HttpMediaTypeNotSupportedException and the HttpMessageNotReadableException. A corresponding Spring 3.2 based implementation can be found in the GlobalControllerExceptionHandler, or continue reading for yet another implementation.

ResponseEntityExceptionHandler

The above example works well, but it can be hard to identify the Spring MVC specific exceptions to implement a common error response handling strategy for them. One way of overcoming this problem is to extend the ResponseEntityExceptionHandler class, which was also added to the Spring 3.2 release. Similarly to the DefaultHandlerExceptionResolver, it provides methods for handling the exceptions, but it allows the developer to specify ResponseEntitys as return values (as opposed to the ModelAndViews that are returned by the methods in the DefaultHandlerExceptionResolver). The implementation is still straight forward, create a class, annotate it with the @ControllerAdvice, extend the ResponseEntityExceptionHandler class and override the methods with the exception types that you are interested in:

Comments

  • The @ControllerAdvice annotation is an @Component. As such, an annotated class will be registered as a Spring bean if the package in which it is located in is subject to component scanning.
  • The @ControllerAdvice also supports methods annotated with @InitBinder and @ModelAttribute.

References

6 Comments

  1. Phani

    Thank you boss,
    U saved me

  2. forumssj

    Thanks for the great post! Can you give an example to create a Exception handler using the @ControllerAdvice annotation, to return JSON or Modelview depending on whether the request is ajax or non-ajax

    • Mattias Severson

      Since the ErrorMessage class is annotated with @XmlRootElement it is eligible for XML serialization. However, if you use <mvc:annotation-driven /> (or @EnableWebMvc), then there a range of different HttpMessageConverters enabled for you automatically. Additionally, if you add Jackson to your classpath then you can get JSON as well. Which converter will be used is controlled by the client by specifying the accept in the server request (use application/json for JSON). More information can be found in the Spring reference docs, e.g. enable mvc config and message conversion.

      • forumssj

        I have no problem with configuration for returning JSON. I have already done that. I am working on an application where the Spring controller returns Modelview or JSON depending on the request type. eg for ajax reguest return JSON and for a non-ajax request return a ModelView and I am looking for a common way to handle exceptions in both cases. But I think this is not possible with ResponseEntityExceptionHandler.

        • Mattias Severson

          I have not looked into any details, but here are a some suggestions that may help you:

          • Extend the DefaultHandlerExceptionResolver which methods returns ModelAndView (instead of my example in which the CustomResponseEntityExceptionHandler‘s methods return ResponseEntity).
          • Try including a ModelAndView when returning an ResponseEntity from the ResponseEntityExceptionHandler, e.g.
            new ResponseEntity(new ModelAndView(...), headers, status)
          • If different resources are involved, perhaps you can use custom @ExceptionHandler for each controller.
          • Have you read Paul Chapman’s blog post about Content Negotiation Using Views? Perhaps you will find some ideas there.
          • Otherwise, I suggest that you post a question on the Spring Forum or on Stack Overflow.

Leave a Reply