Tomcat 的 ErrorPage 实现原理分析
使用Tomcat,一定见到过404,500的时候,见到过Tomcat提供的错误页面,例如请求的资源找不到的时候,响应状态码为404,这个时候的错误页面是这样的:
这些错误页面是 如何生成及定位展示的 ,如果我们要 自定义一些错误页面 ,又要怎么做呢?今天我们一起来看看,Tomcat中提供的ErrorPage处理。
我们以Manager应用为例,来了解整个流程。
-
首先,Manager应用的web.xml中,包含如下关于ErrorPage的配置:
这些配置,在应用部署,解析web.xml文件的时候,都会生成ErrorPage对应,设置到对应Context应用的属性中。
for (ErrorPage errorPage : webxml.getErrorPages().values() ) {
context. addErrorPage (errorPage);
}
(web.xml文件解析及应用部署,可以参考前面的文章《 WEB应用是怎样部署的?》)
下面的代码,就是StandardContext添加ErrorPage的内容。我们看到,按照对应配置的状态码,每个对应一个errorPage对象,保存到Map中。
上面是context设置errorPage。在应用请求处理时,如果对应的资源不存在,或者产生异常时,此时就需要根据配置的errorPage,进行对应的展示。
例如下面的代码,是在对应用的资源树上查找,如果不存在就会直接以 SC_NOT_FOUND 响应。
sendError的作用,是进行一个error标识的设置,便于后面对于该状态的使用。在其内部主要进行errorState和状态码的设置。
public boolean setError() {
boolean result = errorState .compareAndSet(0, 1 );
if (result) {
Wrapper wrapper = getRequest().getWrapper();
if (wrapper != null) {
wrapper.incrementErrorCount();
}
}
return result;
}
我们前面介绍过,整个请求处理流程中,会在Pipeline中包含一系列的Valve。在资源找不到或者异常产生时,Valve的后处理逻辑中, 在StandardHostValve中,后处理的代码有如下的逻辑
// Look for (and render if found) an application level error page
if (response. isErrorReportRequired ()) {
if (t != null) {
throwable(request, response, t);
} else {
status (request, response);
}
}
这里的response.isErrorReportRequired获取的就是上面对于error标识的设置。在这里,如果有异常产生会打印异常,如果是其它的状态标识,会走status处理。
status方法中,最主要的逻辑,就是根据状态码,获取对应配置的错误页面。
int statusCode = response.getStatus();
ErrorPage errorPage = context.findErrorPage(statusCode);
if (errorPage == null) {
// Look for a default error page
errorPage = context.findErrorPage(0);
}
status的代码,在获取对应的errorPage之后,会进行一个custom的操作,定向到对应的错误页面。代码如下:
从上面的代码中,我们看到定向的方式,使用的是RequestDispatcher. forward
ServletContext servletContext =
request.getContext().getServletContext();
RequestDispatcher rd =
servletContext.getRequestDispatcher( errorPage.getLocation() );
rd.forward(request.getRequest(), response.getResponse());
上面就是整个自定义errorPage的处理流程,概括起来,就是根据指定的错误页面位置,再在对应的状态码match的时候,进行forward的处理。(重定向和转发,可以参见前面的文章《关于重定向和转发》)所以此处,我们可以进行一个个性化的404或者500的页面处理。
当然,ErrorPage的配置中,可以声明一个exceptionType,在指定的异常产生时,对应到相应的page上面,原理类似。
除上之外,需要说明的一点是,有些应用中,我们并没有显示的指定errorPage,但是在应用的请求中,依然可以看到熟悉的Tomcat错误页面。这个是因为,在Pipeline中,还有一个Vavle, ErrorReportValve
依然走前面的错误处理流程时,在根据错误码获取errorPage的时候,因为没有对应的配置,custom处理就跳过了。整个错误展示留给后面的ErrorReportValve。
在它的report方法中,对于状态码小于400的不做处理,其他的就会生成我们熟悉的错误页面,同时,加上对应的状态码,提示信息,如果有相应的stackTrace,会遍历显示到 页面上。
所以,这个其实是Tomcat代码里硬编码的一个。内容基本是这个样子:
所以,对于未指定errorPage的应用,看到的是相同样式的错误页面的,就是因为上面的原因。
相关阅读
阀门(Valve)常打开 | Tomcat的AccessLogValve介绍
http://www.tuicool.com/articles/UfEnqie
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
2015-10-20 git branch(转)
2015-10-20 Git提交代码的处理流程(转)
2015-10-20 Do a “git export” (like “svn export”)?(转)
2015-10-20 最有价值的信息就是这样的信息:大象是绳子,大象是扇子,大象是柱子…… 这样的信息往往是扭曲的,残缺的,隐晦不明的(转)
2015-10-20 说服他。说不服再按着他的去办(转)
2015-10-20 动手学习TCP:数据传输(转)
2015-10-20 应用程序框架实战十三:DDD分层架构之我见(转)