Spring 3.2 to the rescue - it introduced a new annotation called @ControllerAdvice (http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/new-in-3.2.html#new-in-3.2-webmvc-controller-advice) that defines methods that applies to all @RequestMapping rest url methods, and in particular to help with exceptions with @ExceptionHandler. We can use this to have a generic exception handling solution to have all ajax calls return json, and all ModelAndView return html.
- @ControllerAdvice allows you to define ONE controller class to handle ALL exceptions that could occur (no specific exceptions defined needed)
- You can control the HTTP status code returned
- You can control the json message returned by grabbing the exception message from the specific exception that occurred
Here's how you do it:
- Your controllers won't change, the code will still continue to throw any number of specific exceptions:
- Write ONE new Controller, this will act as your ControllerAdvice controller. Notice:
- the class is annotated with @ControllerAdvice
- @ExceptionHandler is annotated above your generic "handleException" function
- handleException function takes generic Exception e as a parameter
- it checks the accept header to see how the controller function was invoked (via ajax or via form submit; randomException or mavException, respectively). *Update* We have to check for null first in case the request doesn't specify a response type expected. If the request doesn't specify it, then simply return text/html.
- It returns json or html
@Controller
public class ExceptionController {
@RequestMapping(value = "/randomException", method = RequestMethod.GET)
public String randomException(Authentication auth, HttpServletRequest request) throws Exception {
throw new NumberFormatException(" " +
"Test ControllerAdvice randomException[" + "NumberFormatException" + "]");
[...]
}
@RequestMapping(value = "/mavException", method = RequestMethod.GET)
public ModelAndView modelAndViewException(Authentication auth, HttpServletRequest request) throws Exception {
throw new UnexpectedRollbackException("Test ControllerAdvice Exception for mav");
[...]
}
}
@ControllerAdvice
public class CherryShoeControllerAdvice {
/*
* Handles JSON and HTML
*/
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleException(HttpServletRequest request, HttpServletResponse response, Exception e) throws IOException {
String acceptHeader = request.getHeader("Accept");
// If Accept header exists, check if it expects a response of type json, otherwise just return text/html
// Use apache commons lang3 to escape json values
if(acceptHeader.contains("application/json")) {
// return as JSON
String jsonString =
"{\"success\": false, \"message\": \"" + StringEscapeUtils.escapeJson(e.getMessage()) + "\" }";
System.out.println("In handleGeneric" + e.getMessage());
return jsonString;
} else {
//return as HTML
response.setContentType("text/html");
return response.toString();
}
}
}
BTW - if you wanted to specify each type of exception going to a separate @ExceptionHandler you could.
BTW - The main cons for using pre-Spring 3.2 @ExceptionHandler (http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-exceptionhandlers) by itself are you have to:
- Define an ExceptionHandler for EACH controller (or have each controller inherit from one common base)
- Define EACH exception type to be handled – Or have each controller throw the specific type of exception expected
super..really it helped me solving my issue with spring mvc and angularjs...thank you very much...
ReplyDeletesuper..really it helped me solving my issue with spring mvc and angularjs...thank you very much...
ReplyDeleteSuper! Glad this article helped you.
DeleteThanks for the tip on ConrollerAdvice. Works great!
ReplyDeleteFantastic! Glad this article helped you.
DeleteHello. Im using spring 3.2.7 and it's not working. I have Controllers in differents packages. It only works if the @ControllerAdvice class is in the same package with a particular Controller
ReplyDelete