wake-up-neo.com

javax.faces.application.ViewExpiredException: Die Ansicht konnte nicht wiederhergestellt werden

Ich habe eine einfache Anwendung mit containergesteuerter Sicherheit geschrieben. Das Problem ist, wenn ich mich anmelde und eine andere Seite öffne, auf der ich mich abmelde, dann zur ersten Seite zurückkehre und auf einen Link usw. klicke oder die Seite aktualisiere, auf der diese Ausnahme auftritt. Ich denke, es ist normal (oder vielleicht auch nicht :)), weil ich mich abgemeldet habe und die Sitzung zerstört ist. Was kann ich tun, um den Benutzer zum Beispiel zu index.xhtml oder login.xhtml umzuleiten und zu verhindern, dass er diese Fehlerseite/-nachricht sieht?

Mit anderen Worten, wie kann ich andere Seiten nach dem Abmelden automatisch zur Index-/Anmeldeseite umleiten?

Hier ist es:

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)
164
l245c4l

Einführung

Das ViewExpiredException wird geworfen, wenn javax.faces.STATE_SAVING_METHOD Auf server (Standardeinstellung) gesetzt ist und der Endbenutzer eine HTTP POST) -Anforderung für eine Ansicht sendet über <h:form> mit <h:commandLink>, <h:commandButton> oder <f:ajax>, während der zugehörige Ansichtsstatus in der Sitzung nicht mehr verfügbar ist.

Der Ansichtsstatus wird als Wert eines ausgeblendeten Eingabefeldes javax.faces.ViewState Des <h:form> Identifiziert. Wenn die Methode zum Speichern des Status auf server festgelegt ist, enthält diese nur die Ansichtsstatus-ID, die auf einen serialisierten Ansichtsstatus in der Sitzung verweist. Wenn die Sitzung aus irgendeinem Grund abgelaufen ist (entweder auf dem Server oder auf dem Client abgelaufen oder das Sitzungscookie im Browser aus irgendeinem Grund nicht mehr verwaltet wird oder durch Aufrufen von HttpSession#invalidate() auf dem Server oder aufgrund von a serverspezifischer Fehler bei Sitzungscookies wie in WildFly ) bekannt, dann ist der Status der serialisierten Ansicht in der Sitzung nicht mehr verfügbar und der Endbenutzer erhält diese Ausnahme. Weitere Informationen zur Funktionsweise der Sitzung finden Sie unter Wie funktionieren Servlets? Instanziierung, Sitzungen, freigegebene Variablen und Multithreading .

Es gibt auch eine Begrenzung für die Anzahl der Ansichten, die JSF in der Sitzung speichert. Wenn das Limit erreicht ist, läuft die zuletzt verwendete Ansicht ab. Siehe auch com.Sun.faces.numberOfViewsInSession vs com.Sun.faces.numberOfLogicalViews .

Wenn die Methode zum Speichern des Status auf client gesetzt ist, enthält das ausgeblendete Eingabefeld javax.faces.ViewState Stattdessen den gesamten Status der serialisierten Ansicht, sodass der Endbenutzer nach Ablauf der Sitzung kein ViewExpiredException erhält . Dies kann jedoch weiterhin in einer Cluster-Umgebung auftreten ("FEHLER: MAC wurde nicht überprüft" ist symptomatisch) und/oder wenn auf der Clientseite ein implementierungsspezifisches Timeout konfiguriert ist und/oder wenn der Server den AES-Schlüssel während des Neustarts neu generiert Siehe auch ViewExpiredException in einer Cluster-Umgebung abrufen, während die Methode zum Speichern des Status auf Client festgelegt ist und die Benutzersitzung gültig ist wie man das Problem löst.

Stellen Sie unabhängig von der Lösung sicher, dass Sie nicht enableRestoreView11Compatibility Verwenden. Der ursprüngliche Ansichtsstatus wird überhaupt nicht wiederhergestellt. Im Grunde wird die Ansicht und alle damit verbundenen Beans mit dem Gültigkeitsbereich von Grund auf neu erstellt, wodurch alle ursprünglichen Daten (Status) verloren gehen. Da sich die Anwendung verwirrend verhält ("Hey, wo sind meine Eingabewerte .. ??"), ist dies für die Benutzererfahrung sehr schlecht. Verwenden Sie stattdessen lieber zustandslose Ansichten oder <o:enableRestorableView>, Damit Sie sie nur für eine bestimmte Ansicht anstatt für alle Ansichten verwalten können.

Bezüglich warum muss JSF den Ansichtsstatus speichern, gehen Sie zu folgender Antwort: Warum speichert JSF den Status von UI-Komponenten auf dem Server?

Vermeiden von ViewExpiredException bei der Seitennavigation

Um ViewExpiredException zu vermeiden, wenn z.B. Nach dem Abmelden zurücknavigieren, wenn die Statusspeicherung auf server gesetzt ist, das Umleiten der POST nach dem Abmelden reicht nicht aus. Sie müssen den Browser auch anweisen, nicht die dynamischen JSF-Seiten zwischenspeichern, andernfalls zeigt der Browser sie möglicherweise aus dem Cache an, anstatt eine neue vom Server anzufordern, wenn Sie eine GET-Anforderung darauf senden (z. B. durch die Schaltfläche "Zurück").

Das ausgeblendete Feld javax.faces.ViewState Der zwischengespeicherten Seite enthält möglicherweise einen Wert für die Ansichtsstatus-ID, der in der aktuellen Sitzung nicht mehr gültig ist. Wenn Sie POST (Befehlsverknüpfungen/Schaltflächen) anstelle von GET (reguläre Verknüpfungen/Schaltflächen) für die Navigation von Seite zu Seite verwenden (ab), und klicken Sie auf eine solche Befehlsverknüpfung/Schaltfläche die zwischengespeicherte Seite, dann wird dies wiederum mit einem ViewExpiredException fehlschlagen.

Um eine Umleitung nach dem Abmelden in JSF 2.0 auszulösen, fügen Sie entweder <redirect /> Zum fraglichen <navigation-case> Hinzu (falls vorhanden), oder fügen Sie ?faces-redirect=true Zum Wert outcome hinzu .

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

oder

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

Um den Browser anzuweisen, die dynamischen JSF-Seiten nicht im Cache zu speichern, erstellen Sie ein Filter, das dem Servlet-Namen des FacesServlet zugeordnet ist, und fügen die erforderlichen Antwort-Header hinzu, um den Browser-Cache zu deaktivieren. Z.B.

@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);
    }

    // ...
}

Vermeiden von ViewExpiredException bei Seitenaktualisierung

Um ViewExpiredException beim Aktualisieren der aktuellen Seite zu vermeiden, wenn die Statusspeicherung auf server gesetzt ist, müssen Sie nicht nur sicherstellen, dass Sie die Navigation von Seite zu Seite ausschließlich mit GET (regulär) durchführen Links/Buttons), aber Sie müssen auch sicherstellen, dass Sie ausschließlich Ajax verwenden, um die Formulare einzureichen. Wenn Sie das Formular trotzdem synchron (ohne Ajax) senden, sollten Sie entweder die Ansicht zustandslos machen (siehe späterer Abschnitt) oder eine Umleitung nach POST (siehe vorheriger Abschnitt) senden Sektion).

Ein ViewExpiredException bei der Seitenaktualisierung ist in der Standardkonfiguration ein sehr seltener Fall. Dies kann nur passieren, wenn das Limit für die Anzahl der Ansichten, die JSF in der Sitzung speichert, überschritten wird. Dies ist nur dann der Fall, wenn Sie dieses Limit manuell zu niedrig eingestellt haben oder ständig neue Ansichten im "Hintergrund" erstellen (z. B. durch eine schlecht implementierte Ajax-Umfrage auf derselben Seite oder durch eine schlecht implementierte 404-Umfrage) Fehlerseite bei fehlerhaften Bildern derselben Seite). Siehe auch com.Sun.faces.numberOfViewsInSession vs com.Sun.faces.numberOfLogicalViews für Details zu diesem Limit. Eine andere Ursache besteht darin, dass doppelte JSF-Bibliotheken im Laufzeitklassenpfad in Konflikt zueinander stehen. Die korrekte Vorgehensweise zur Installation von JSF ist in nsere JSF-Wiki-Seite beschrieben.

Behandlung von ViewExpiredException

Wenn Sie eine unvermeidbare ViewExpiredException nach einer POST) - Aktion auf einer beliebigen Seite behandeln möchten, die bereits in einem Browser-Tab/-Fenster geöffnet wurde, während Sie in einem anderen Tab/abgemeldet sind Fenster, dann möchten Sie einen error-page für das in web.xml spezifizieren, das zu einer "Ihre Sitzung ist abgelaufen" Seite geht.

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

Verwenden Sie bei Bedarf einen Meta-Refresh-Header auf der Fehlerseite, falls Sie tatsächlich umleiten weiter zur Startseite oder Anmeldeseite möchten.

<!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>

(das 0 in content stellt die Anzahl der Sekunden vor der Umleitung dar, 0 bedeutet also "sofort umleiten", Sie können zB 3 Damit der Browser 3 Sekunden mit der Weiterleitung wartet)

Beachten Sie, dass für die Behandlung von Ausnahmen bei Ajax-Anforderungen ein spezielles ExceptionHandler erforderlich ist. Siehe auch Session-Timeout und ViewExpiredException-Behandlung bei JSF/PrimeFaces-Ajax-Anforderung . Ein Live-Beispiel finden Sie unter OmniFaces FullAjaxExceptionHandler showcase page (dies gilt auch für Nicht-Ajax-Anforderungen).

Beachten Sie auch, dass Ihre "allgemeine" Fehlerseite auf <error-code> Von 500 Anstatt auf <exception-type> Von z. Java.lang.Exception Oder Java.lang.Throwable, Andernfalls werden alle in ServletException eingeschlossenen Ausnahmen wie ViewExpiredException weiterhin auf der allgemeinen Fehlerseite angezeigt. Siehe auch ViewExpiredException in Java.lang.Throwable-Fehlerseite in web.xml .

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

Staatenlose Ansichten

Eine völlig andere Alternative besteht darin, JSF-Ansichten im statuslosen Modus auszuführen. Auf diese Weise wird nichts vom JSF-Status gespeichert und die Ansichten laufen nie ab, sondern werden bei jeder Anforderung von Grund auf neu erstellt. Sie können zustandslose Ansichten aktivieren, indem Sie das Attribut transient von <f:view> Auf true setzen:

<f:view transient="true">

</f:view>

Auf diese Weise erhält das ausgeblendete Feld javax.faces.ViewState In Mojarra einen festen Wert von "stateless" (Haben Sie MyFaces zu diesem Zeitpunkt noch nicht überprüft). Beachten Sie, dass diese Funktion eingeführt in Mojarra 2.1.19 und 2.2.0 war und in älteren Versionen nicht verfügbar ist.

Die Konsequenz ist, dass Sie keine View-Scoped-Beans mehr verwenden können. Sie verhalten sich jetzt wie Bohnen mit Anforderungsbereich. Einer der Nachteile besteht darin, dass Sie den Status selbst verfolgen müssen, indem Sie an versteckten Eingaben und/oder losen Anforderungsparametern herumspielen. Betroffen sind hauptsächlich Formulare mit Eingabefeldern mit den Attributen rendered, readonly oder disabled, die von Ajax-Ereignissen gesteuert werden.

Beachten Sie, dass <f:view> Nicht unbedingt in der gesamten Ansicht eindeutig sein und/oder sich nur in der Master-Vorlage befinden muss. Es ist auch völlig legitim, es erneut zu deklarieren und in einem Vorlagen-Client zu verschachteln. Es "erweitert" dann im Grunde das übergeordnete Element <f:view>. Z.B. in der Master-Vorlage:

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

und im Template Client:

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

Sie können sogar den <f:view> In einen <c:if> Einwickeln, um die Bedingung zu erfüllen. Beachten Sie, dass dies für die Ansicht gesamt gilt, nicht nur für die verschachtelten Inhalte, wie z. B. <h:form> Im obigen Beispiel.

Siehe auch


Nicht mit dem konkreten Problem verwandt ist die Verwendung von HTTP POST für die reine Navigation von Seite zu Seite nicht sehr benutzerfreundlich/SEO-freundlich: In JSF 2.0 sollten Sie <h:link> Oder <h:button> Den <h:commandXxx> Für eine einfache Vanilla-Navigation von Seite zu Seite vorziehen.

Anstelle von z.B.

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

besser machen

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

Siehe auch

335
BalusC

Haben Sie versucht, Ihrem web.xml Die folgenden Zeilen hinzuzufügen?

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

Ich fand das sehr effektiv, als ich auf dieses Problem stieß.

55
Mike GH

Bevor Sie web.xml ändern, müssen Sie zunächst sicherstellen, dass Ihr ManagedBean implements Serializable:

@ManagedBean
@ViewScoped
public class Login implements Serializable {
}

Vor allem, wenn Sie MyFaces verwenden

5
ZuzEL

Vermeiden Sie mehrteilige Formulare in Richfaces:

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

Wenn Sie RichFaces verwenden, wurde festgestellt, dass Ajax-Anforderungen in mehrteiligen Formularen bei jeder Anforderung eine neue Ansichts-ID zurückgeben.

Wie man debuggt:

Bei jeder Ajax-Anforderung wird eine Ansichts-ID zurückgegeben. Dies ist in Ordnung, solange die Ansichts-ID immer dieselbe ist. Wenn Sie bei jeder Anforderung eine neue Ansichts-ID erhalten, liegt ein Problem vor, das behoben werden muss.

3
tak3shi

Sie können Ihren eigenen Ajax ExceptionHandler oder eigene Primefaces-Erweiterungen verwenden

Aktualisiere deine faces-config.xml

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

Füge folgenden Code in deine jsf Seite ein

...
<pe:ajaxErrorHandler />
...
0
myset

Wenn unsere Seite für x Zeit inaktiv ist, läuft die Ansicht ab und löst javax.faces.application.ViewExpiredException aus, um dies zu verhindern. Eine Lösung besteht darin, CustomViewHandler zu erstellen, der ViewHandler erweitert, und die restoreView-Methode zu überschreiben, an die alle anderen Methoden delegiert werden Elternteil

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;
    }

}   

Dann müssen Sie es zu Ihrer faces-config.xml hinzufügen

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

Vielen Dank für die ursprüngliche Antwort auf den folgenden Link: http://www.gregbugaj.com/?p=164

0
user4924157

Ich habe den folgenden Fehler erhalten: javax.faces.application.ViewExpiredException.Wenn ich verschiedene Anforderungen verwende, habe ich auch nach dem Neustart des Servers festgestellt, dass diese dieselbe JsessionId haben. Das liegt also am Browser-Cache. Schließen Sie einfach den Browser und versuchen Sie es, es wird funktionieren.

0
rahul