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応答の最初の部分がすでにクライアント側に送信されていることを意味します。これはノーリターンのポイントです。すでに送信されたバイトをクライアントから取り戻すことはできません。

書き込まれたコンテンツがバッファサイズを超えると、応答は通常自動コミットされます。バッファサイズは、servletcontainerとFaceletsの構成に応じて、デフォルトで通常2KBになります。平均的なHTML<head>はすでに1〜2KBを占めています。<body>したがって、JSFがレンダリングを開始する前に、またはその一部のみを応答する前に、応答がすでにコミットされているという大きな変化があります。

ただし、特定のケースでは、もう1つの原因があります。複数の未処理の例外を受け取った場合、whileナビゲートした後にループを中止せず、次の例外の処理を続行するため、問題が発生します。1つのリクエストに複数の応答(エラーページ)を返すことはできません。すべての例外を収集して1回だけナビゲートするか、最初の例外の後でループを中止する必要があります。

いずれの場合も、レンダリング応答中にスローされた例外の処理は、応答が(自動)コミットされていない場合にのみ可能です。応答がすぐに自動コミットされるのを防ぐために、いくつかのアプローチを試すことができます。

  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 に答える