178

コンテナ管理のセキュリティを備えた簡単なアプリケーションを作成しました。問題は、ログインしてログアウトした別のページを開いた後、最初のページに戻り、リンクなどをクリックするか、ページを更新すると、この例外が発生することです。ログアウトしてセッションが破壊されたため、正常であると思います(またはそうではないかもしれません:))。ユーザーをたとえばindex.xhtmlまたはlogin.xhtmlにリダイレクトし、そのエラーページ/メッセージが表示されないようにするにはどうすればよいですか?

つまり、ログアウト後に他のページをインデックス/ログインページに自動的にリダイレクトするにはどうすればよいですか?

ここにあります:

javax.faces.application.ViewExpiredException: viewId:/index.xhtml - View /index.xhtml could not be restored.
    at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:212)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:110)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1523)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
    at filter.HttpHttpsFilter.doFilter(HttpHttpsFilter.java:66)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:277)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
    at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:325)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:226)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165)
    at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791)
    at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693)
    at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954)
    at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170)
    at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
    at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
    at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
    at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
    at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309)
    at java.lang.Thread.run(Thread.java:619)
4

10 に答える 10

362

序章

が(デフォルト)に設定され、エンドユーザーがビューでHTTP POSTリクエストを送信するViewExpiredExceptionたびに、がスローされますが、関連付けられたビューステートはセッションで使用できなくなります。javax.faces.STATE_SAVING_METHODserver<h:form><h:commandLink><h:commandButton><f:ajax>

ビューステートは、の非表示の入力フィールドjavax.faces.ViewStateの値として識別されます<h:form>。状態保存方法をに設定するとserver、これには、セッション内のシリアル化されたビュー状態を参照するビュー状態IDのみが含まれます。したがって、次のいずれかの理由でセッションが期限切れまたは不在の場合...

  • セッションオブジェクトがサーバーでタイムアウトしました
  • クライアントでセッションCookieがタイムアウトしました
  • セッションCookieがクライアントで削除されます
  • HttpSession#invalidate()サーバーで呼び出されます
  • SameSite=NoneセッションCookieに欠落している(したがって、サードパーティのサイト(支払いなど)がコールバックURLを介してサイトに戻るときにChromeはそれらを送信しません)

...その後、シリアル化されたビューステートはセッションで使用できなくなり、エンドユーザーはこの例外を受け取ります。セッションの動作を理解するには、サーブレットの動作も参照してください。インスタンス化、セッション、共有変数、マルチスレッド

JSFがセッションに保存するビューの量にも制限があります。制限に達すると、最も使用頻度の低いビューが期限切れになります。com.sun.faces.numberOfViewsInSessionとcom.sun.faces.numberOfLogicalViewsも参照してください。

状態保存方法をに設定するclientと、javax.faces.ViewState非表示の入力フィールドには代わりにシリアル化されたビューステート全体が含まれるためViewExpiredException、セッションの有効期限が切れてもエンドユーザーはを取得できません。ただし、クラスタ環境(「エラー:MACが検証しませんでした」は症状があります)、および/または構成されたクライアント側の状態に実装固有のタイムアウトがある場合、および/またはサーバーが再起動中にAESキーを再生成する場合に発生する可能性があります、状態保存メソッドがクライアントに設定され、ユーザーセッションが有効な方法でクラスター環境でViewExpiredExceptionを取得するも参照してください。

解決策に関係なく、を使用しないenableRestoreView11Compatibilityようにしてください。元のビューステートはまったく復元されません。基本的に、ビューと関連するすべてのビュースコープのBeanを最初から再作成するため、元のデータ(状態)がすべて失われます。アプリケーションは紛らわしい方法で動作するため(「ねえ、私の入力値はどこにありますか.. ??」)、これはユーザーエクスペリエンスに非常に悪い影響を及ぼします。ステートレスビューを使用するか、<o:enableRestorableView>代わりに、すべてのビューではなく特定のビューでのみ管理できるようにすることをお勧めします。

JSFがビューステートを保存する必要がある理由については、次の回答に進んでください。JSFがUIコンポーネントの状態をサーバーに保存する理由は何ですか。

ページナビゲーションでのViewExpiredExceptionの回避

ViewExpiredException状態保存がに設定されているときにログアウト後に戻るなどの場合を回避するにはserver、ログアウト後にPOSTリクエストをリダイレクトするだけでは不十分です。また、動的JSFページをキャッシュしないようにブラウザに指示する必要があります。そうしないと、ブラウザがGETリクエストを送信するときに(たとえば、戻るボタンで)サーバーに新しいページをリクエストする代わりに、キャッシュからページを表示する場合があります。

キャッシュされたページのjavax.faces.ViewState非表示フィールドには、現在のセッションでは無効になったビューステートID値が含まれている可能性があります。ページ間ナビゲーションにGET(通常のリンク/ボタン)の代わりにPOST(コマンドリンク/ボタン)を(ab)使用していて、キャッシュされたページでそのようなコマンドリンク/ボタンをクリックすると、これが順番に行われますで失敗しViewExpiredExceptionます。

JSF 2.0でログアウトした後にリダイレクトを起動<redirect />するには<navigation-case>、問題の(存在する場合)に追加?faces-redirect=trueするか、outcome値に追加します。

<h:commandButton value="Logout" action="logout?faces-redirect=true" />

また

public String logout() {
    // ...
    return "index?faces-redirect=true";
}

動的JSFページをキャッシュしないようにブラウザに指示するにFilterは、のサーブレット名にマップされFacesServlet、ブラウザのキャッシュを無効にするために必要な応答ヘッダーを追加するを作成します。例えば

@WebFilter(servletNames={"Faces Servlet"}) // Must match <servlet-name> of your FacesServlet.
public class NoCacheFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        if (!req.getRequestURI().startsWith(req.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)
            res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
            res.setHeader("Pragma", "no-cache"); // HTTP 1.0.
            res.setDateHeader("Expires", 0); // Proxies.
        }

        chain.doFilter(request, response);
    }

    // ...
}

ページ更新時のViewExpiredExceptionの回避

ViewExpiredException状態保存がに設定されているときに現在のページが更新されないserverようにするには、GET(通常のリンク/ボタン)だけでページ間ナビゲーションを実行していることを確認する必要があるだけでなく、次のことも確認する必要があります。フォームの送信にajaxのみを使用していること。とにかくフォームを同期的に(非ajaxで)送信する場合は、ビューをステートレスにするか(後のセクションを参照)、POST後にリダイレクトを送信する(前のセクションを参照)のが最善です。

ページ上の更新を行うことViewExpiredExceptionは、デフォルト構成では非常にまれなケースです。これは、JSFがセッションに保存するビューの量の制限に達した場合にのみ発生する可能性があります。そのため、手動で制限を低く設定しすぎた場合、または「バックグラウンド」で新しいビューを継続的に作成している場合にのみ発生します(たとえば、同じページに不適切に実装されたajaxポーリングによって、または不適切に実装された404によって)同じページの壊れた画像のエラーページ)。その制限の詳細については、com.sun.faces.numberOfViewsInSessionとcom.sun.faces.numberOfLogicalViewsも参照してください。もう1つの原因は、ランタイムクラスパスに重複するJSFライブラリが互いに競合していることです。JSFをインストールする正しい手順は、JSFwikiページに概説されています。

ViewExpiredExceptionの処理

ViewExpiredException別のタブ/ウィンドウでログアウトしているときに、あるブラウザのタブ/ウィンドウですでに開いている任意のページでPOSTアクションを実行した後、やむを得ない処理を行う場合は、そのページのを指定error-pageします。 web.xml「セッションがタイムアウトしました」ページに移動します。例えば

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/WEB-INF/errorpages/expired.xhtml</location>
</error-page>

実際にホームページまたはログインページにさらにリダイレクトする場合は、必要に応じてエラーページのメタリフレッシュヘッダーを使用してください。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Session expired</title>
        <meta http-equiv="refresh" content="0;url=#{request.contextPath}/login.xhtml" />
    </head>
    <body>
        <h1>Session expired</h1>
        <h3>You will be redirected to login page</h3>
        <p><a href="#{request.contextPath}/login.xhtml">Click here if redirect didn't work or when you're impatient</a>.</p>
    </body>
</html>

0incontentはリダイレクトまでの秒数を表します。つまり、0「すぐにリダイレクトする」ことを意味します。たとえば3、ブラウザにリダイレクトを3秒間待機させることができます)

ajaxリクエスト中に例外を処理するには、特別なが必要であることに注意してくださいExceptionHandlerJSF /PrimeFacesajaxリクエストでのセッションタイムアウトとViewExpiredException処理も参照してください。OmniFacesのFullAjaxExceptionHandlerショーケースページでライブの例を見つけることができます(これはajax以外のリクエストも対象としています)。

また、「一般的な」エラーページは、egまたは<error-code>500代わりににマップする必要があることに注意してください。そうしないと、などでラップされたすべての例外が一般的なエラーページに表示されます。web.xmlのjava.lang.Throwableエラーページに示されているViewExpiredExceptionも参照してください。<exception-type>java.lang.Exceptionjava.lang.ThrowableServletExceptionViewExpiredException

<error-page>
    <error-code>500</error-code>
    <location>/WEB-INF/errorpages/general.xhtml</location>
</error-page>

ステートレスビュー

まったく異なる代替手段は、JSFビューをステートレスモードで実行することです。このように、JSF状態は何も保存されず、ビューが期限切れになることはありませんが、要求ごとに最初から再構築されるだけです。transientの属性を<f:view>次のように設定すると、ステートレスビューをオンにできますtrue

<f:view transient="true">

</f:view>

このようにして、javax.faces.ViewState隠しフィールドはMojarraの固定値を取得し"stateless"ます(この時点ではMyFacesをチェックしていません)。この機能はMojarra2.1.19および2.2.0で導入されたものであり、古いバージョンでは使用できないことに注意してください。

その結果、ビュースコープのBeanを使用できなくなります。これで、リクエストスコープのBeanのように動作します。欠点の1つは、非表示の入力や緩い要求パラメーターをいじって、自分で状態を追跡する必要があることです。rendered主に、、readonlyまたはdisabledajaxイベントによって制御される属性を持つ入力フィールドを持つフォームが影響を受けます。

<f:view>ビュー全体で一意である必要はなく、マスターテンプレートのみに存在する必要もありません。テンプレートクライアントで再宣言してネストすることも完全に合法です。それは基本的に親を「拡張」し<f:view>ます。例:マスターテンプレート:

<f:view contentType="text/html">
    <ui:insert name="content" />
</f:view>

およびテンプレートクライアント:

<ui:define name="content">
    <f:view transient="true">
        <h:form>...</h:form>
    </f:view>
</f:view>

をでラップして条件付きにすることもでき<f:view>ます<c:if>。上記の例のように、ネストされたコンテンツだけでなく、ビュー全体に適用されることに注意してください。<h:form>

も参照してください


具体的な問題とは関係なく、純粋なページ間ナビゲーションにHTTP POSTを使用することは、ユーザーやSEOにあまり適していません。JSF 2.0では、単純なバニラページ間ナビゲーション用のものより<h:link>も実際に優先する必要があります。<h:button><h:commandXxx>

だから例えばの代わりに

<h:form id="menu">
    <h:commandLink value="Foo" action="foo?faces-redirect=true" />
    <h:commandLink value="Bar" action="bar?faces-redirect=true" />
    <h:commandLink value="Baz" action="baz?faces-redirect=true" />
</h:form>

やるほうがいい

<h:link value="Foo" outcome="foo" />
<h:link value="Bar" outcome="bar" />
<h:link value="Baz" outcome="baz" />

も参照してください

于 2010-09-04T15:57:25.193 に答える
56

以下の行をに追加してみましたweb.xmlか?

<context-param>
   <param-name>com.sun.faces.enableRestoreView11Compatibility</param-name>
   <param-value>true</param-value>
</context-param>

この問題が発生したとき、これは非常に効果的であることがわかりました。

于 2012-05-14T12:50:17.560 に答える
5

web.xmlを変更する前に、まず行う必要があるのは、ManagedBeanを確認することですimplements Serializable

@ManagedBean
@ViewScoped
public class Login implements Serializable {
}

特にMyFacesを使用する場合

于 2013-07-28T14:58:09.213 に答える
3

Richfacesではマルチパートフォームを避けてください。

<h:form enctype="multipart/form-data">
    <a4j:poll id="poll" interval="10000"/>
</h:form>

Richfacesを使用している場合、マルチパートフォーム内のajaxリクエストは、リクエストごとに新しいビューIDを返すことがわかりました。

デバッグ方法:

各ajaxリクエストで、ビューIDが返されます。これは、ビューIDが常に同じである限り問題ありません。リクエストごとに新しいビューIDを取得する場合は、問題があり、修正する必要があります。

于 2017-03-24T12:24:55.257 に答える
0

独自のカスタムAjaxExceptionHandlerまたはprimefaces-extensionsを使用することをお勧めします

あなたのfaces-config.xmlを更新します

...
<factory>
  <exception-handler-factory>org.primefaces.extensions.component.ajaxerrorhandler.AjaxExceptionHandlerFactory</exception-handler-factory>
</factory>
...

jsfページに次のコードを追加します

...
<pe:ajaxErrorHandler />
...
于 2014-01-11T10:00:50.047 に答える
0

次のエラーが発生していました:javax.faces.application.ViewExpiredException。異なるリクエストを使用すると、サーバーを再起動した後でも、同じJsessionIdを持つリクエストが見つかりました。したがって、これはブラウザのキャッシュによるものです。ブラウザを閉じて試してみてください。動作します。

于 2014-10-05T14:39:12.300 に答える
0

ページがx時間アイドル状態になると、ビューが期限切れになり、javax.faces.application.ViewExpiredExceptionがスローされて、これが発生しないようにします。1つの解決策は、ViewHandlerを拡張するCustomViewHandlerを作成し、restoreViewメソッドをオーバーライドすることです。親

import java.io.IOException;
import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;

public class CustomViewHandler extends ViewHandler {
    private ViewHandler parent;

    public CustomViewHandler(ViewHandler parent) {
        //System.out.println("CustomViewHandler.CustomViewHandler():Parent View Handler:"+parent.getClass());
        this.parent = parent;
    }

    @Override 
    public UIViewRoot restoreView(FacesContext facesContext, String viewId) {
    /**
     * {@link javax.faces.application.ViewExpiredException}. This happens only  when we try to logout from timed out pages.
     */
        UIViewRoot root = null;
        root = parent.restoreView(facesContext, viewId);
        if(root == null) {
            root = createView(facesContext, viewId);
        }
        return root;
    }

    @Override
    public Locale calculateLocale(FacesContext facesContext) {
        return parent.calculateLocale(facesContext);
    }

    @Override
    public String calculateRenderKitId(FacesContext facesContext) {
        String renderKitId = parent.calculateRenderKitId(facesContext);
        //System.out.println("CustomViewHandler.calculateRenderKitId():RenderKitId: "+renderKitId);
        return renderKitId;
    }

    @Override
    public UIViewRoot createView(FacesContext facesContext, String viewId) {
        return parent.createView(facesContext, viewId);
    }

    @Override
    public String getActionURL(FacesContext facesContext, String actionId) {
        return parent.getActionURL(facesContext, actionId);
    }

    @Override
    public String getResourceURL(FacesContext facesContext, String resId) {
        return parent.getResourceURL(facesContext, resId);
    }

    @Override
    public void renderView(FacesContext facesContext, UIViewRoot viewId) throws IOException, FacesException {
        parent.renderView(facesContext, viewId);
    }

    @Override
    public void writeState(FacesContext facesContext) throws IOException {
        parent.writeState(facesContext);
    }

    public ViewHandler getParent() {
        return parent;
    }

}   

次に、それをfaces-config.xmlに追加する必要があります

<application>
    <view-handler>com.demo.CustomViewHandler</view-handler>
</application>

以下のリンクの元の回答に感謝します: http ://www.gregbugaj.com/?p = 164

于 2015-05-21T10:09:17.653 に答える
-2

この行をweb.xmlに追加してくださいそれは私のために働きます

<context-param>
        <param-name>org.ajax4jsf.handleViewExpiredOnClient</param-name> 
        <param-value>true</param-value>     
    </context-param>
于 2014-06-13T15:21:05.027 に答える
-2

私は自分でこの問題に遭遇し、アプリケーション上のすべてのリクエストをフィルタリングするために作成したフィルターの副作用が原因であることに気付きました。特定のリクエストのみを選択するようにフィルターを変更するとすぐに、この問題は発生しませんでした。アプリケーションでそのようなフィルターをチェックし、それらがどのように動作するかを確認するのは良いことかもしれません。

于 2014-06-25T09:40:26.360 に答える
-3

次の構成をweb.xmlに追加すると、解決されました。

<context-param>
    <param-name>com.sun.faces.numberOfViewsInSession</param-name>
    <param-value>500</param-value>
</context-param>
<context-param>
    <param-name>com.sun.faces.numberOfLogicalViews</param-name>
    <param-value>500</param-value>
</context-param>
于 2012-11-10T09:50:47.487 に答える