如有 index.jsp 頁,當出現後伺服器端異異常時要轉向到 errorPage.jsp,並在 errorPage.jsp 中把對應錯誤資訊顯示出來。我們需要在這兩個頁面分別加上指令 errorPage="errorPage.jsp" 和 isErrorPage="true"。
index.jsp
- <%@page errorPage="errorPage.jsp" %>
-
- <%
- throw new Exception("exception from jsp");
- %>
<%@page errorPage="errorPage.jsp" %></p><p><%<br /> throw new Exception("exception from jsp");<br />%>
errorPage.jsp
- <%@page isErrorPage="true" %>
- <%
- out.println(exception);
-
- //根據 Exception 類型及描述顯示可理解的資訊
- %>
<%@page isErrorPage="true" %><br /><%<br /> out.println(exception);</p><p> //根據 Exception 類型及描述顯示可理解的資訊<br />%>
errorPage.jsp 中必須加上 isErrorPage="true" 指令,才會存在內建變數 exception。直接存取 errorPage.jsp 沒有異常時,exception 為 null。
瀏覽器中用 URL http://localhost:8080/test/index.jsp 訪問,頁面輸出
java.lang.Exception: exception from index.jsp.
那實現機制是什麼呢?仍在常見的 Tomcat 容器中運行結果來分析
查看 Tomcat 編譯出的 index_jsp.java 進而追溯到(代碼片斷)
org.apache.jasper.runtime.PageContextImpl.doHandlePageException(Throwable t)() 中的部分代碼
- request.setAttribute("javax.servlet.jsp.jspException", t);
- request.setAttribute("javax.servlet.error.request_uri",
- ((HttpServletRequest) request).getRequestURI());
- forward(errorPageURL);
-
- Object newException = request.getAttribute("javax.servlet.error.exception");
-
- // t==null means the attribute was not set.
- if( (newException!= null) && (newException==t) ) {
- request.removeAttribute("javax.servlet.error.exception");
- }
-
- request.removeAttribute("javax.servlet.error.exception");
- request.removeAttribute("javax.servlet.jsp.jspException");
request.setAttribute("javax.servlet.jsp.jspException", t);<br /> request.setAttribute("javax.servlet.error.request_uri",<br /> ((HttpServletRequest) request).getRequestURI());<br /> forward(errorPageURL);</p><p> Object newException = request.getAttribute("javax.servlet.error.exception");</p><p> // t==null means the attribute was not set.<br /> if( (newException!= null) && (newException==t) ) {<br /> request.removeAttribute("javax.servlet.error.exception");<br /> }</p><p> request.removeAttribute("javax.servlet.error.exception");<br /> request.removeAttribute("javax.servlet.jsp.jspException");
同樣要翻看 Tomcat 編譯出的 errorPage_jsp.java 可看到(代碼片斷)
Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request); // JSP 中
org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request) 中的部分代碼
- Throwable error = (Throwable) request.getAttribute(SERVLET_EXCEPTION);
- if (error == null) {
- error = (Throwable) request.getAttribute(JSP_EXCEPTION);
- (error != null) {
- request.setAttribute(SERVLET_EXCEPTION, error);
Throwable error = (Throwable) request.getAttribute(SERVLET_EXCEPTION);<br /> if (error == null) {<br /> error = (Throwable) request.getAttribute(JSP_EXCEPTION);<br /> if (error != null) {<br /> request.setAttribute(SERVLET_EXCEPTION, error);
最終都是以 SERVLET_EXCEPTION 把異常設定到 request 中
在 JspRuntimeLibrary 定義了這兩個常量字串
private static final String SERVLET_EXCEPTION = "javax.servlet.error.exception";
private static final String JSP_EXCEPTION = "javax.servlet.jsp.jspException";
如果要在自己的代碼中,如 Servlet、Filter、Struts 的 RequestProcessor 或 Struts Action 中出現異常也能轉向到 errorPage.jsp,並能複用原來的 errorPage.jsp 中顯示錯誤資訊的代碼,該如何應用上面的分析成果呢?
下面以 Struts Action 為例來說明,應用於其他地方可借鑒。為什麼用了 Struts 卻不用 Struts 提供的異常處理模型呢?也是無奈的,維護的舊系統,框上了 Struts,其他各處只能慢慢來換。
只要在 Struts Action 中的 execute() 方法中寫上
- Throwable t = new Exception("exception from Struts Action.");
- request.setAttribute("javax.servlet.error.jspException", t);
-
- //或者在 struts-config.xml 中配置一個全域錯誤頁
- return new ActionForward("/errorPage.jsp");
Throwable t = new Exception("exception from Struts Action.");<br /> request.setAttribute("javax.servlet.error.jspException", t);</p><p> //或者在 struts-config.xml 中配置一個全域錯誤頁<br /> return new ActionForward("/errorPage.jsp");
進一步深入你可以自訂一個 Struts 的異常處理類來做這個事情。
這時候還需要自已在 errorPage.jsp 的最後一行加上 java 代碼:
request.removeAttribute("javax.servlet.error.exception");
去除 request 中的異常屬性,不然沒法用 errorPage.jsp 顯示錯誤,而代之為的是那個常見的
HTTP Status 500 -
description The server encountered an internal error () that prevented it from fulfilling this request.
這時候瀏覽器中通過 URL http://localhost:8080/test/test.do 訪問,頁面輸出
java.lang.Exception: exception from Struts Action.
最後嘗試把這個項目發布到 Websphere Application Server (WAS) 5.1 下,訪問 http://localhost:9080/test/test.do,頁面輸出 null,沒取到異常!
沒什麼好辦法,還是反編譯 WAS 產生的 JSP 代碼中,發現到 WAS 5.1 的 PageContext 的實作類別也是 org.apache.jasper.runtime.PageContextImpl,在 WAS_HOME/lib/webcontainer.jar 包中。在這個 PageContextImpl 類中也是設定
request.setAttribute("javax.servlet.jsp.jspException", t);
但是WAS 5.1 下 errorPage.jsp 直接通過 request 來取異常:
throwable = (Throwable)httpservletrequest.getAttribute("javax.servlet.jsp.jspException");
不再理會 request 中的 javax.servlet.error.exception 屬性值的。
因此只要注意一點,在 Tomcat 中,Action 裡既可以設定
request.setAttribute("javax.servlet.error.exception", t);
也可以是
request.setAttribute("javax.servlet.jsp.jspException", t);
當然在 WAS 5.1 下的 errorPage.jsp 中就不需要
request.removeAttribute("javax.servlet.error.exception");
那要不要用
request.removeAttribute("javax.servlet.jsp.jspException");
移除 request 中的 javax.servlet.jsp.jspException 屬性呢?也用不著啦。
Tomcat 下和 WAS 下的 jsp 頁面的主要差別是在它們的基類不同,Tomcat 下的 JSP 頁是繼承自 org.apache.jasper.runtime.HttpJspBase,而 WAS 5.1 下的 JSP 頁是繼承自 com.ibm.ws.webcontainer.jsp.runtime.HttpJspBase。
考慮在能相容兩種平台的做法應該是,在 Action 中統一用
request.setAttribute("javax.servlet.jsp.jspException", t);
設定異常,然後在 errorPage.jsp 補上一行
request.removeAttribute("javax.servlet.error.exception");
就 OK 啦。