Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a proper global error handler with Spring MVC / Spring Boot

I'm writing a web application with Spring 4.0.4 and Spring Boot 1.0.2 using Tomcat as embedded web container and I want to implement a global exception handling which intercepts all exceptions and logs them in a specific way. My simple requirements are:

  • I want to globally handle all exceptions which are not already processed somewhere else (In a controller exception handler for example). I want to log the message and I want to display a custom error message to the user.
  • I don't want Spring or the web container to log any errors by itself because I want to do this myself.

So far my solution looks like this (Simplified, no logging and no redirection to an error view):

@Controller @RequestMapping("/errors") public class ErrorHandler implements EmbeddedServletContainerCustomizer {     @Override     public void customize(final ConfigurableEmbeddedServletContainer factory)     {         factory.addErrorPages(new ErrorPage("/errors/unexpected"));         factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/errors/notfound"));     }      @RequestMapping("unexpected")     @ResponseBody     public String unexpectedError(final HttpServletRequest request)     {         return "Exception: " + request.getAttribute("javax.servlet.error.exception");     }      @RequestMapping("notfound")     @ResponseBody     public String notFound()     {         return "Error 404";     } } 

The result is that exceptions thrown in controllers are correctly handled by the unexpectedError method and 404 status codes are handled by the notFound method. So far so good, but I have the following problems:

  • Tomcat or Spring (not sure who is responsible) is still logging the error message. I don't want that because I want to log it myself (with additional information) and I don't want duplicate error messages in the log. How can I prevent this default logging?
  • The way I access the exception object doesn't seem right. I fetch if from the request attribute javax.servlet.error.exception. And that's not even the thrown exception, it is an instance of org.springframework.web.util.NestedServletException and I have to dive into this nested exception to fetch the real one. I'm pretty sure there is an easier way but I can't find it.

So how can I solve these problems? Or maybe the way I implemented this global exception handler is completely wrong and there is a better alternative?

like image 290
kayahr Avatar asked May 10 '14 11:05

kayahr


People also ask

How do I create a global exception handler in spring boot?

Exception Handler The @ExceptionHandler is an annotation used to handle the specific exceptions and sending the custom responses to the client. Define a class that extends the RuntimeException class. You can define the @ExceptionHandler method to handle the exceptions as shown.

What is the correct way of creating Spring Global exception Handler class?

By default the methods in an @ControllerAdvice apply globally to all Controllers. We can create a class and add @ControllerAdvice annotation on top. Then add @ExceptionHandler methods for each type of specific exception classes in it. Notice we extended the exception handler class with ResponseEntityExceptionHandler.

How do you handle authentication and exception handling in Spring Boot and Spring MVC applications?

Spring MVC provides exception handling for your web application to make sure you are sending your own exception page instead of the server-generated exception to the user. The @ExceptionHandler annotation is used to detect certain runtime exceptions and send responses according to the exception.


1 Answers

Have a look at ControllerAdvice You could do something like this:

@ControllerAdvice public class ExceptionHandlerController {      public static final String DEFAULT_ERROR_VIEW = "error";      @ExceptionHandler(value = {Exception.class, RuntimeException.class})     public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) {             ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);          mav.addObject("datetime", new Date());         mav.addObject("exception", e);         mav.addObject("url", request.getRequestURL());         return mav;     } } 
like image 78
Michiel Avatar answered Sep 17 '22 17:09

Michiel