Tomcat錯誤頁面支持http響應嗎和Java中異常類兩種形式。使用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,如果沒有定義錯誤頁面會生成一個0的頁面,地址默認為/error,findErrorPage(0) 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的應用,看到的是相同樣式的錯誤頁面的,就是因為上面的原因。