3

我为 JSF 编写了一个自定义异常处理程序来记录异常并导航到要向用户显示的错误页面。不幸的是,在处理程序中调用“handleNavigation”时,我得到一个 IllegalStateException“在提交响应后无法调用 sendRedirect()”。任何想法,我做错了什么?

我的处理程序:

public class MyExceptionHandler extends ExceptionHandlerWrapper {
    private Logger           log            = ...;
    public  ExceptionHandler wrappedHandler = null;

    public MyExceptionHandler (ExceptionHandler wrappedHandler) {
        this.wrappedHandler = wrappedHandler;
    }

    @Override
    public ExceptionHandler getWrapped() {
        return wrappedHandler;
    }   

    @Override
    public void handle() throws FacesException {
        Iterator<ExceptionQueuedEvent> iter         = null;
        ExceptionQueuedEvent           event        = null;
        ExceptionQueuedEventContext    eventContext = null;
        FacesContext                   facesContext = null;       

        iter = getUnhandledExceptionQueuedEvents().iterator();      

        while (iter.hasNext()) {
            try {
                event        = iter.next();
                eventContext = (ExceptionQueuedEventContext) event.getSource(); 

                log.error("JSF Exception aufgetreten", eventContext.getException());          

                facesContext = FacesContext.getCurrentInstance();                    

                // !!!!!!!! Exception occurs here !!!!!!!!!!!
                facesContext
                   .getApplication()
                   .getNavigationHandler()
                   .handleNavigation(facesContext, null, "error");

                facesContext.renderResponse();        

            } catch (RuntimeException ex) {
                throw ex; // just to set break point
            } finally {
                iter.remove();
            }
          }
          getWrapped().handle();       
    }
}

faces-config.xml 中的导航定义

<navigation-rule>
    <from-view-id>*</from-view-id>
    <navigation-case>
        <from-outcome>error</from-outcome>
        <to-view-id>/faces/error.xhtml</to-view-id>
        <redirect/>
    </navigation-case>
</navigation-rule>
4

1 回答 1

2

正在处理的异常显然是在渲染响应阶段抛出的,此时 HTTP 响应已经提交。提交的响应意味着 HTTP 响应的第一部分(包括标头)已经发送到客户端。这是一个不归路。您不能从客户端取回已经发送的字节。

当写入的内容超出通常默认为 ~2KB 的缓冲区大小时,通常会自动提交响应,具体取决于 servletcontainer 和 Facelets 配置。平均 HTML<head>已占用 1~2KB。所以变化很大,响应在 JSF 开始渲染之前就已经提交了,<body>或者只是其中的一小部分。

然而,在您的特定情况下,还有一个原因:当您收到多个未处理的异常时,您也会遇到麻烦,因为您在while导航后并没有中止循环,而是继续处理下一个异常。您不能对单个请求返回多个响应(错误页面)。您应该收集所有异常并仅导航一次,或者在第一个异常之后中止循环。

在任何情况下,只要响应不是(自动)提交的,就可以处理在渲染响应期间抛出的异常。您可以尝试几种方法来防止响应过早自动提交:

  1. 将 Facelets 缓冲区大小设置为最大 HTML 响应的大小。例如 64KB:

    <context-param>
        <param-name>javax.faces.FACELETS_BUFFER_SIZE</param-name>
        <param-value>65535</param-value><!-- 64KB -->
    </context-param>
    
  2. 在呈现视图之前执行异常敏感的业务工作(即不要在 GET 请求期间构造的 bean 的(后)构造函数中执行它):

    <f:event type="preRenderView" listener="#{bean.init}" />
    

至于在 JSF 中处理异常,您可能会发现OmniFaces FullAjaxExceptionHandler很有帮助。你可以在这里找到它的源代码。

也可以看看:

于 2013-03-25T10:13:21.813 回答