Reputation: 4228
I'm using SpringMVC and I want to handle exception on rest controller. My controller usually write a json in response output, but when exception occurs I'm unable to catch it and tomcat html page is returned.
How I can catch global exceptions and return appropriate response based on "accept" parameter in request?
Upvotes: 2
Views: 7721
Reputation: 33759
The @ControllerAdvice annotation is a new annotation that was added in the Spring 3.2 release. From the reference docs:
Classes annotated with @ControllerAdvice can contain @ExceptionHandler, @InitBinder, and @ModelAttribute methods and those will apply to @RequestMapping methods across controller hierarchies as opposed to the controller hierarchy within which they are declared. @ControllerAdvice is a component annotation allowing implementation classes to be auto-detected through classpath scanning.
Example:
@ControllerAdvice
class GlobalControllerExceptionHandler {
// Basic example
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
ErrorMessage handleException(FirstException ex) {
ErrorMessage errorMessage = createErrorMessage(ex);
return errorMessage;
}
// Multiple exceptions can be handled
@ExceptionHandler({SecondException.class, ThirdException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
ErrorMessage handleException() {
ErrorMessage errorMessage = createErrorMessage(...);
return errorMessage;
}
// Returning a custom response entity
@ExceptionHandler
ResponseEntity<ErrorMessage> handleException(OtherException ex) {
ErrorMessage errorMessage = createErrorMessage(...);
ResponseEntity<ErrorMessage> responseEntity = new ResponseEntity<ErrorMessage>(errorMessage, HttpStatus.BAD_REQUEST);
return responseEntity;
}
}
Basically, it allows you to catch the specified exceptions, creates a custom ErrorMessage
(this is your custom error class that Spring will serialize to the response body according to the Accept
header) and in this example sets the response status to 400 - Bad Request
. Note that the last example returns a ResponseEntity (and is not annotated with @ResponseBody
) which allows you to specify response status and other response headers programmatically. More information about the @ExceptionHandler
can be found in the reference docs, or in a blog post that I wrote some time ago.
Update: added more examples based on comments.
Upvotes: 14
Reputation: 21784
Another approach (what I'm using) is to create a global exception handler and tell Spring that it should be used. Then you don't have to duplicate your logic or to extend the same base-controller as you have to do when annotating a controller method with @ExceptionHandler
. Here's a simple example.
public class ExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse response, Object o, Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // Or some other error code
ModelAndView mav = new ModelAndView(new MappingJackson2JsonView());
mav.addObject("error", "Something went wrong: \"" + e.getMessage() + "\"");
return mav;
}
}
And in the <something>-servlet.xml
you assign it as your wanted exceptionResolver:
<!-- Define our exceptionHandler as the resolver for our program -->
<bean id="exceptionResolver" class="tld.something.ExceptionHandler" />
Then all exceptions will be sent to your Exceptionhandler, and there you can take a look at the request and determine how you should reply to the user. In my case I'm using Jackson.
Upvotes: 1