Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpringMVC handle errors in rest controller

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?

like image 587
Premier Avatar asked Dec 17 '25 19:12

Premier


2 Answers

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.

like image 112
matsev Avatar answered Dec 20 '25 08:12

matsev


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.

like image 29
Matsemann Avatar answered Dec 20 '25 08:12

Matsemann



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!