1. 程式人生 > >Tomcat 的 ErrorPage 實現原理分析

Tomcat 的 ErrorPage 實現原理分析

esp 轉發 isp ORC code compare on() sta ger

https://www.cnblogs.com/softidea/p/5981766.html

使用Tomcat,一定見到過404,500的時候,見到過Tomcat提供的錯誤頁面,例如請求的資源找不到的時候,響應狀態碼為404,這個時候的錯誤頁面是這樣的:

技術分享圖片

這些錯誤頁面是 如何生成及定位展示的 ,如果我們要 自定義一些錯誤頁面 ,又要怎麽做呢?今天我們一起來看看,Tomcat中提供的ErrorPage處理。

我們以Manager應用為例,來了解整個流程。

  1. 首先,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的應用,看到的是相同樣式的錯誤頁面的,就是因為上面的原因。

Tomcat 的 ErrorPage 實現原理分析