JSP 的 errorPage 指令异常转向错误页的实现机制及应用

如有 index.jsp 页,当出现后服务器端异异常时要转向到 errorPage.jsp,并在 errorPage.jsp 中把对应错误信息显示出来。我们需要在这两个页面分别加上指令 errorPage="errorPage.jsp"isErrorPage="true"

index.jsp

  1. <%@page errorPage="errorPage.jsp" %>  
  2.   
  3. <%   
  4.     throw new Exception("exception from jsp");   
  5. %>  

<%@page errorPage="errorPage.jsp" %>

<%
    throw new Exception("exception from jsp");
%>

errorPage.jsp

  1. <%@page isErrorPage="true" %>  
  2. <%   
  3.     out.println(exception);   
  4.        
  5.     //根据 Exception 类型及描述显示可理解的信息   
  6. %>  

<%@page isErrorPage="true" %>
<%
    out.println(exception);
    
    //根据 Exception 类型及描述显示可理解的信息
%>

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)() 中的部分代码

  1. request.setAttribute("javax.servlet.jsp.jspException", t);   
  2. request.setAttribute("javax.servlet.error.request_uri",   
  3.         ((HttpServletRequest) request).getRequestURI());   
  4. forward(errorPageURL);   
  5.   
  6. Object newException = request.getAttribute("javax.servlet.error.exception");   
  7.   
  8. // t==null means the attribute was not set.   
  9. if( (newException!= null) && (newException==t) ) {   
  10.       request.removeAttribute("javax.servlet.error.exception");   
  11. }   
  12.   
  13. request.removeAttribute("javax.servlet.error.exception");   
  14. request.removeAttribute("javax.servlet.jsp.jspException");  

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");

同样要翻看 Tomcat 编译出的 errorPage_jsp.java 可看到(代码片断)

Throwable exception = org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request); // JSP 中

org.apache.jasper.runtime.JspRuntimeLibrary.getThrowable(request) 中的部分代码

  1. Throwable error = (Throwable) request.getAttribute(SERVLET_EXCEPTION);   
  2. if (error == null) {   
  3.     error = (Throwable) request.getAttribute(JSP_EXCEPTION);   
  4. (error != null) {   
  5.         request.setAttribute(SERVLET_EXCEPTION, error);  

Throwable error = (Throwable) request.getAttribute(SERVLET_EXCEPTION);
    if (error == null) {
        error = (Throwable) request.getAttribute(JSP_EXCEPTION);
 if (error != null) {
            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() 方法中写上

  1. Throwable t = new Exception("exception from Struts Action.");   
  2. request.setAttribute("javax.servlet.error.jspException", t);   
  3.   
  4. //或者在 struts-config.xml 中配置一个全局错误页   
  5. return new ActionForward("/errorPage.jsp");  

Throwable t = new Exception("exception from Struts Action.");
    request.setAttribute("javax.servlet.error.jspException", t);
    
    //或者在 struts-config.xml 中配置一个全局错误页
    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 啦。

你可能感兴趣的:(tomcat,jsp,exception,servlet,struts,null)