如何使用Spring MVC/Spring Boot编写正确的全局错误处理程序

kay*_*ahr 39 java spring tomcat spring-mvc spring-boot

我正在编写一个使用Spring 4.0.4和Spring Boot 1.0.2的Web应用程序,使用Tomcat作为嵌入式Web容器,我想实现一个全局异常处理,它拦截所有异常并以特定方式记录它们.我的简单要求是:

  • 我想全局处理尚未在其他地方处理的所有异常(例如在控制器异常处理程序中).我想记录消息,我想向用户显示自定义错误消息.
  • 我不希望Spring或Web容器自己记录任何错误,因为我想自己这样做.

到目前为止,我的解决方案看起来像这样(简化,没有日志记录,也没有重定向到错误视图):

@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";
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是该unexpectedError方法正确处理控制器中抛出的异常,并且该方法处理404状态代码notFound.到目前为止很好,但我有以下问题:

  • Tomcat或Spring(不确定谁负责)仍在记录错误消息.我不希望这样,因为我想自己记录(附加信息),我不希望日志中出现重复的错误消息.如何防止此默认日志记录?
  • 我访问异常对象的方式似乎不对.我从request属性中获取if javax.servlet.error.exception.这甚至不是抛出的异常,它是一个实例,org.springframework.web.util.NestedServletException我必须深入到这个嵌套异常中来获取真实异常.我很确定有一种更简单的方法,但我找不到它.

那么我该如何解决这些问题呢?或者我实现这个全局异常处理程序的方式是完全错误的,还有更好的选择吗?

小智 55

看看ControllerAdvice您可以这样做:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,此解决方案还将捕获SpringException之类的Spring Security异常.因此,登录后重定向到受保护资源将不起作用.说明:输入受保护资源通常会将您重定向到登录页面.登录后,您应该返回受保护的资源(Spring Security通过捕获AuthenticationException来处理它),但是当您捕获所有类似的异常时,Spring Security将不再捕获它. (3认同)
  • 最干净的解决方案.另一个值得添加的可能是@ResponseStatus到500,它将按预期返回HTTP 500中的响应. (2认同)