SpringMVC ErrorPage粗解

2021-02-10  本文已影响0人  丑人林宗己

起因

最近观察生产日志发现偶尔会看到一个/error接口的错误异常,但是从异常堆栈信息里没有找到任何关于真实发生错误的接口路径,以至于排查问题时无从下手。

通过Postman复现一下,效果如下:

image.png

带着如下几个问题来拆分ErrorPage的实现方式:

ErrorController

通过查找,定位到自己代码依赖着一个自实现的common包,其中有个CustomerErrorController,其访问路径为:

${server.error.path:${error.path:/error}}

在配置文件没有配置server.error.path以及error.path时,路径为/error,逻辑上符合复现的场景,通过debug也确实可以证实。

CustomerErrorController网上找最终发现顶级接口就是ErrorController,利用IDEA工具可以快速看到默认实现的类

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController{  ...  }

@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
    return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
            this.errorViewResolvers);
}

ServerProperties中找到ErrorProperties,其中默认配置的path/error,与CustomerErrorController的路径是相同的。

ErrorMvcAutoConfiguration

/// 核心的组件
ErrorPageCustomizer
ErrorPageRegistrarBeanPostProcessor
ErrorPageFilter
TomcatEmbeddedServletContainerFactory
TomcatErrorPage

// 在ErrorPageFilter中找到
private void setErrorAttributes(HttpServletRequest request, int status,
        String message) {
    request.setAttribute(ERROR_STATUS_CODE, status);
    request.setAttribute(ERROR_MESSAGE, message);
    // 获取到uri就可以知道是哪个接口触发了这段跳转至/error接口
    request.setAttribute(ERROR_REQUEST_URI, request.getRequestURI());
}

// 这几个参数在自定义的ErrorController中是可以读取到,
// 但是通过Debug发现并没有运行到ErrorPageFilter,
// 意味着类似的代码可能在另外的地方也有一份

回到前文提到的两个问题。

什么场景会触发ErrorPage?由于没有找到真正核心的代码,参考ErrorPageFilter的代码如下:(想要一探究竟,考虑从DispatchServlet的流程上去找)

private void doFilter(HttpServletRequest request, HttpServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        ErrorWrapperResponse wrapped = new ErrorWrapperResponse(response);
        try {
            chain.doFilter(request, wrapped);
            if (wrapped.hasErrorToSend()) {
                handleErrorStatus(request, response, wrapped.getStatus(),
                        wrapped.getMessage());
                response.flushBuffer();
            }
            else if (!request.isAsyncStarted() && !response.isCommitted()) {
                response.flushBuffer();
            }
        }
        catch (Throwable ex) {
            Throwable exceptionToHandle = ex;
            if (ex instanceof NestedServletException) {
                exceptionToHandle = ((NestedServletException) ex).getRootCause();
            }
            handleException(request, response, wrapped, exceptionToHandle);
            response.flushBuffer();
        }
    }

ErrorPage包括哪些核心组件?参照ErrorMvcAutoConfiguration

上一篇 下一篇

猜你喜欢

热点阅读