Spring Boot Web异常处理机制

前言

自己在开发实际项目中,需要这样的全局错误处理需求:对html请求错误返回通用错误页面,屏蔽详细信息。对REST请求错误返回JSON数据,包含错误信息。通过查阅文档,我无法找到解决方案,因此我不得不从上到下理解Spring Boot的错误处理逻辑,寻找解决方案。

版本:Spring Boot 1.5.x。

servlet的errorpage机制

首先看看最底层的Servlet API提供的错误处理机制。

错误原因

Servlet容器捕获到了异常或使用HttpServletResponse.sendError()返回HTTP错误状态。

错误处理

Servlet提供了errorpage的错误处理机制,提供了基于status code/exception type到resource的映射的错误处理方式。

<error-page>
    <error-code>404</error-code>
    <location>/pageNotFound</location>
</error-page>

这种映射通过转发实现,会在request域中附带相关错误信息以便渲染:

javax.servlet.error.exception 错误异常参数
javax.servlet.error.exception_type 错误异常类型
...

参考:https://tomcat.apache.org/tomcat-7.0-doc/servletapi/constant-values.html

问题是web.xml已经被时代抛弃,而servlet3.0也没有提供相关的Java接口来提供该功能

Spring Boot Web错误处理

由于Servlet缺乏相关的Java API来满足该功能,因此Spring Boot提供了自己封装的一套Java API。采取了相同的处理逻辑:

  • 支持对status code/exception进行错误处理
  • 采取转发的错误处理方式

处理机制

Spring Boot Web错误处理机制是独立于MVC框架的,包括:

  • ErrorPageRegistrar 错误页面注册者
  • ErrorPageRegistry 错误页面注册表
  • ErrorPage 错误页面

嵌入式Servlet容器

对于jar包和war包,Spring Boot分别提供了不同的实现方式。当采取嵌入式Servlet容器时,容器工厂实现了错误页面注册表:

public interface ConfigurableEmbeddedServletContainer extends ErrorPageRegistry {
}

容器工厂在初始化时会注册ErrorPages,这往往依赖于具体的底层实现,如Tomcat会调用其相关API转换并注册为其原生的ErrorPage实现。以Tomcat为例:

TomcatEmbeddedServletContainerFactory:

protected void configureContext(Context context,
      ServletContextInitializer[] initializers) {
   //...
   for (ErrorPage errorPage : getErrorPages()) {
      new TomcatErrorPage(errorPage).addToContext(context);
   }
   //...
}

TomcatErrorPage:

public void addToContext(Context context) {
   Assert.state(this.nativePage != null,
         "Neither Tomcat 7 nor 8 detected so no native error page exists");
   if (ClassUtils.isPresent(ERROR_PAGE_CLASS, null)) {
      org.apache.tomcat.util.descriptor.web.ErrorPage errorPage = (org.apache.tomcat.util.descriptor.web.ErrorPage) this.nativePage;
      errorPage.setLocation(this.location);
      errorPage.setErrorCode(this.errorCode);
      errorPage.setExceptionType(this.exceptionType);
      context.addErrorPage(errorPage);
   }
   else {
      callMethod(this.nativePage, "setLocation", this.location, String.class);
      callMethod(this.nativePage, "setErrorCode", this.errorCode, int.class);
      callMethod(this.nativePage, "setExceptionType", this.exceptionType,
            String.class);
      callMethod(context, "addErrorPage", this.nativePage,
            this.nativePage.getClass());//注册为容器原生的错误页面
   }
}

这其实是容器对servlet errorpage的底层实现,委托给容器对Servlet API的支持来实现错误处理。

war包

而如果是独立部署的war包,Spring Boot则使用Filter来实现。具体为ErrorPageFilter:

public class ErrorPageFilter implements Filter, ErrorPageRegistry {
//...
}

配置类

以上介绍了Spring Boot Web提供的类似Servlet原生的错误处理机制。下面介绍具体的实现。对于Spring MVC,错误处理实现实现可以在ErrorMvcAutoConfiguration类找到。其主要配置了:

  • ErrorPageCustomizer,它会注册一个global ErrorPage,匹配/error路径,作为缺省设置。
  • 包含一个WhitelabelErrorViewConfiguration,它配置了缺省的html错误视图。
  • 包含一个DefaultErrorViewResolverConfiguration,它将常见的错误状态码映射为/error/xxx视图名。
  • 配置了BasicErrorController,接收路径为/error的请求。

最上层SpringMVC Web错误处理

mvc对处理器异常进行了处理,并提供了@ControllerAdvice来实现处理器层对Exception的处理。

总结

Servlet层提供了对错误状态码和异常的处理。错误状态码通过request.sendError()触发,处理逻辑实现由servlet容器提供。

由于servlet3.0缺乏errorpage的Java API支持,Spring Boot封装了一套。对于嵌入式Servlet容器,调用servlet容器对errorpage的底层支持实现;对于war包,通过Servlet Filter来实现。

Spring Boot的默认处理机制是转发给映射到/error的Controller来处理。

Spring MVC本身在Controller层又提供了异常处理机制。

参考:
spring boot-27.1.9 Error Handling

猜你喜欢

转载自www.cnblogs.com/redreampt/p/10757179.html