2

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?

1

3 Answers 3

14

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Good answer! @ControllerAdvice is one of the best things updated in Spring 3.2! You can also put more than one mapping to @ExceptionHandler({Exception1.class, Exception2.class}). Maybe is interesting to update the post information, in my opinion of course.
Can one programmatically set the response status with this solution? I need that for my usecase, and as can be seen with my solution it's possible there. But thanks for sharing the new way of doing it, will come in handy.
Ok, now it works. I've already implemented this solution but it doesn't works. Now i followed your example and it works. Thank you.
1

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.

Comments

0

the ExceptionHandler annotation is doing hwat you want.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.