在web.xml的java.lang.Throwable错误页面中显示的ViewExpiredException。

18

我正在开发一个JSF网络应用程序,在其中如果视图过期,我需要打开一个“会话已过期”页面,但对于其他所有问题都需要打开一个通用的技术错误页面。只有在触发异常时,应用程序才会进入技术错误页面。这是error-page定义:

<error-page> 
    <exception-type>javax.faces.application.ViewExpiredException</exception-type> 
    <location>/jsps/utility/sessionExpired.jsp</location> 
</error-page> 
<error-page> 
    <exception-type>java.lang.Throwable</exception-type> 
    <location>/jsps/utility/technicalError.jsp</location> 
</error-page> 
<error-page>
    <error-code>500</error-code>
    <location>/jsps/utility/technicalError.jsp</location>
</error-page>

我删除了technicalError.jsp错误页面元素,它可以正常工作,但是当我将它们放回去时,无法转到sessionExpired.jsp页面。我该如何告诉Web容器按照正确的顺序评估这些标签,以便显示正确的页面?谢谢。

2个回答

17
这是因为根据JSF规范,ViewExpiredException被包装在ServletException中。以下是JSF 1.2规范第10.2.6.2章的摘录:

10.2.6.2 FacesServlet

调用已保存的Lifecycle实例的execute()方法,并将此请求的FacesContext实例作为参数传递。如果execute()方法引发FacesException,则将其作为ServletException重新抛出,并以FacesException作为根本原因

错误页面的分配方式由Servlet API规范指定。以下是Servlet API规范2.5第9.9.2章的摘录:

SRV.9.9.2错误页面

如果没有包含exception-typeerror-page声明符合类层次结构匹配,并且抛出的异常是ServletException或其子类,则容器提取被包装的异常,如ServletException.getRootCause方法所定义。然后进行第二次匹配,再次尝试与错误页面声明进行匹配,但使用被包装的异常。

在类层次结构中,ServletException已经匹配了Throwable,因此其根本原因不会在第二次匹配中被提取。

要证明这个特定的行为,请将javax.faces.application.ViewExpiredException替换为javax.servlet.ServletException作为<exception-type>,然后重试。您将看到预期的错误页面被显示。

要解决这个问题,只需删除java.lang.Throwablejava.lang.Exception上的错误页面即可。如果没有一个特定于异常的错误页面匹配,那么它仍然会回退到500的错误代码。所以,你所需要的就是这个:

<error-page> 
    <exception-type>javax.faces.application.ViewExpiredException</exception-type> 
    <location>/jsps/utility/sessionExpired.jsp</location> 
</error-page> 
<error-page>
    <error-code>500</error-code>
    <location>/jsps/utility/technicalError.jsp</location>
</error-page>

更新:根据OP的(已删除)评论:要可靠地测试这个,您不能在bean构造函数或方法中执行throw new ViewExpiredException()。这将进而被包装在某些EL异常中。您最终可以在Filter中添加一个打印rootCause的调试行,以便自己查看。

如果您正在使用Eclipse/Tomcat,则快速测试ViewExpiredException的方法如下:

  1. 创建一个带有简单命令按钮的JSF页面,部署并运行它,并在Web浏览器中打开它。
  2. 返回到Eclipse,在Tomcat服务器上右键单击并选择Clean Tomcat Work Directory。这将重新启动Tomcat清除所有序列化会话(重要!仅重新启动Tomcat是不够的)。
  3. 返回到Web浏览器,并在不重新加载页面的情况下按下命令按钮。

1
@PavelS:哦,那样做。只需删除java.lang.Throwable/java.lang.Exception并将500保留为其他异常的通用回退。另请参见答案。 - BalusC
@Shirgill: ServletException 是 Throwable 的一个实例。 - BalusC
@Shirgill:因为第一次通行已经有匹配项了。java.lang.Throwable配置了一个(不正确的)错误页面,而ServletException是Throwable的一个实例。 - BalusC
@BalusC:由于Throwable位于异常和错误层次结构的顶部,您能否给我一个例子,在第一次尝试时不会匹配的情况下。 - Farhan stands with Palestine
@Shirgill:如已回答,请将其替换为<error-code>500 - BalusC
显示剩余19条评论

0

正如BylusC所提到的,部署描述符不能包含任何错误页面处理程序,否则会捕获ViewExpiredException(封装在ServletException中)而不是正确的ViewExpiredException错误页面。

不要忘记验证服务器的部署描述符(例如TomEE/conf/web.xml)是否包含java.lang.Throwable或java.lang.Exception错误页面定义。因为这两个web.xml基本上是合并的。

是的 - 应用程序的web.xml优先于服务器的web.xml,但如果应用程序的web.xml包含

  • 500的错误页面(/error.xhtml)

和服务器的web.xml为

  • 500的错误页面(/500.html)和
  • Throwable的错误页面(/bigerror.html)

那么应用程序上下文将包含这两者的合并:

  • 500的错误页面(/error.xhtml)
  • Throwable的错误页面(/bigerror.html)

并且ViewExpiredExcpetion错误处理将无法正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接