JSF 2.0自定义异常处理程序在handleNavigation上引发IllegalStateException [英] JSF 2.0 Custom Exception Handler throws IllegalStateException on handleNavigation

查看:213
本文介绍了JSF 2.0自定义异常处理程序在handleNavigation上引发IllegalStateException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I wrote a custom exception handler for JSF to log the exception and navigate to an error page to be shown to the user. Unfortunately, I get a IllegalStateException "Cannot call sendRedirect() after the response has been commited" when calling "handleNavigation" inside the Handler. Any ideas, what I'm doing wrong?

我的处理程序:

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 Definition in 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>

推荐答案

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

The exception being handled is apparently been thrown during render response phase, at that point when the HTTP response is already been committed. A committed response means that the first part of the HTTP response, including the headers, is already been sent to the client side. This is a point of no return. You can't take the already sent bytes back from the client.

当写入的内容超出缓冲区大小时,通常会自动提交响应,缓冲区大小通常默认为〜2KB,具体取决于servlet容器和Facelets配置.平均HTML <head>已占用1〜2KB.因此,变化很大,以至于在JSF开始呈现<body>或其中的一小部分之前,响应就已经提交了.

A response will usually be auto-committed when the written content exceeds the buffer size which defaults usually to ~2KB, depending on servletcontainer and Facelets configuration. The average HTML <head> occuppies already 1~2KB. So the change is big that the response is already committed before JSF starts to render the <body> or only a small part of it.

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

In your particular case there's however one more cause: when you receive more than one unhandled exception, then you will also run into trouble, because you're not aborting the while loop after having navigated, but continuing to handling the next exception. You can't return multiple responses (error pages) to a single request. You should either collect all exceptions and navigate only once, or to abort the loop after the first one.

无论如何,只有在未(自动)提交响应的情况下,才有可能处理在渲染响应期间引发的异常.您可以尝试以下几种方法来防止过早自动提交响应:

In any case, handling exceptions which are thrown during render response is only possible as long as the response is not (auto)-committed. You can try several approaches to prevent the response from being auto committed too soon:

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

  1. Set the Facelets buffer size to the size of your largest HTML response. E.g. 64KB:

<context-param>
    <param-name>javax.faces.FACELETS_BUFFER_SIZE</param-name>
    <param-value>65535</param-value><!-- 64KB -->
</context-param>

  • 在呈现视图之前执行异常敏感的业务作业(即,不要在GET请求期间构造的bean的(后)构造函数中执行此操作):

  • Perform the exception-sensitive business job before rendering the view (i.e. don't do it in (post)constructor of a bean which is constructed during a GET request):

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

  • 对于通常在JSF中处理异常的情况,您可能会发现 OmniFaces 此处.

    As to handling with exceptions in JSF in general, you may find OmniFaces FullAjaxExceptionHandler helpful. You can find its source code here.

    • Exception thrown in @PostConstruct causes IllegalStateException in JSF 2.1
    • Error page defined in web.xml is embedded in partially rendered JSF page

    这篇关于JSF 2.0自定义异常处理程序在handleNavigation上引发IllegalStateException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆