为什么要使用JSF ExceptionHandlerFactory而不是<error-page>重定向?

27

到目前为止,我遇到的所有ExceptionHandlerFactory示例都会在捕获ViewExpiredException时将用户重定向到一个viewExpired.jsf页面:

public class ViewExpiredExceptionExceptionHandler extends ExceptionHandlerWrapper {
    private ExceptionHandler wrapped;

    public ViewExpiredExceptionExceptionHandler(ExceptionHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ExceptionHandler getWrapped() {
        return this.wrapped;
    }

    @Override
    public void handle() throws FacesException {
        for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
            ExceptionQueuedEvent event = i.next();
            ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();

            Throwable t = context.getException();
            if (t instanceof ViewExpiredException) {
                ViewExpiredException vee = (ViewExpiredException) t;
                FacesContext facesContext = FacesContext.getCurrentInstance();
                Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap();
                NavigationHandler navigationHandler = facesContext.getApplication().getNavigationHandler();
                try {
                    // Push some useful stuff to the request scope for use in the page
                    requestMap.put("currentViewId", vee.getViewId());
                    navigationHandler.handleNavigation(facesContext, null, "/viewExpired");
                    facesContext.renderResponse();
                } finally {
                    i.remove();
                }
            }
        }

        // At this point, the queue will not contain any ViewExpiredEvents. Therefore, let the parent handle them.
        getWrapped().handle();
    }
}

在我看来,下面这个简单的web.xml配置基本上是相同的,而且更加简单:

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/viewExpired.jsf</location>
</error-page>

这引发了一个问题-为什么要使用ExceptionHandlerFactory?

以上的代码是你写的吗?如果不是,能否注明出处? - Mindwin Remember Monica
2个回答

30

这个特定的例子只做了一个有用的事情:它将视图ID保存为请求属性,以便您可以使用例如

<h:link value="Go back to previous page" outcome="#{currentViewId}" />

但是这并不是非常有用,因为原始请求URI已经可以通过<error-page>的默认请求属性javax.servlet.error.request_uri获取。

<h:outputLink value="#{requestScope['javax.servlet.error.request_uri']}">Go back to previous page</h:outputLink>

然而,自定义的 ExceptionHandler 很有用的一点是可以帮助你处理ajax请求期间出现的异常。默认情况下,它们在客户端只提供了极少的有用反馈。只有在将项目阶段设置为“开发”时,Mojarra 才会显示一个带有异常消息的裸 JavaScript 警报消息。但仅此而已,在“生产”阶段中没有任何反馈形式。通过自定义 ExceptionHandler,您可以解析 web.xml 查找错误页面位置,创建一个新的 UIViewRoot 并强制 JSF 将 ajax 渲染设置为 @all

所以,基本上

String errorPageLocation = "/WEB-INF/errorpages/500.xhtml";
context.setViewRoot(context.getApplication().getViewHandler().createView(context, errorPageLocation));
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();

请参考这个相关的问题:如何正确处理AJAX化组件的JSF 2.0异常和这篇博客:完整的Ajax异常处理程序


OmniFaces的org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory是否可以与通过创建ExceptionHandlerFactory来处理异常一起使用,以便在抛出不应由此工厂包装的异常时转向全局错误页面?文档要求每个应用程序只有一个异常处理程序工厂,“*每个使用JavaServer Faces的Web应用程序必须有一个ExceptionHandlerFactory实例。可以通过调用方式以可移植的方式获取此实例。”顺便说一下,这很好地转发到全局错误页面。 - Tiny
@Tiny:你可以进行扩展。另请参阅Javadoc。重写shouldHandleExceptionRootCause()方法以返回false(并添加致命消息)。 - BalusC

3

当你收到 ViewExpiredException 时,需要视你想要做什么而定。如果你只是想向用户显示错误页面,可以按照你所说的那样操作。

这篇文章展示了如何通过编程拦截ViewExpiredException并对其进行优化处理。


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