javax.face.application.ViewExpiredException: la vue n'a pas pu être restaurée

j'ai écrit application simple avec la sécurité de gestion de conteneur. Le problème est quand je me connecte et ouvre une autre page sur laquelle je me déconnecte, puis je reviens à la première page et je clique sur n'importe quel lien etc ou rafraîchir la page je reçois cette exception. Je suppose que c'est normal (ou peut-être pas:)) parce que je me suis déconnecté et la session est détruite. Que dois-je faire pour rediriger l'utilisateur, par exemple, de l'indice.xhtml ou de connexion.xhtml et le sauver de voir cette page d'erreur / message?

dans les autres mots Comment puis-je rediriger automatiquement d'autres pages vers la page index/login après que je me suis déconnecté?

le voici:

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)
151
demandé sur BalusC 2010-09-04 19:45:31

10 réponses

Introduction

le ViewExpiredException sera lancé à chaque fois que le javax.faces.STATE_SAVING_METHOD est défini à server (par défaut) et l'utilisateur final envoie une requête HTTP POST sur une vue via <h:form> avec <h:commandLink> , <h:commandButton> ou <f:ajax> , alors que l'état de vue associé n'est plus disponible dans la session.

l'état de vue est identifié comme la valeur d'un champ d'entrée caché javax.faces.ViewState du <h:form> . Avec l'état méthode d'enregistrement définie à server , elle contient seulement L'ID d'état de la vue qui renvoie à un État de vue sérialisé dans la session. Ainsi, lorsque la session est Expirée pour une raison quelconque (soit du côté du serveur ou du côté du client, soit le cookie de session n'est plus maintenu pour une raison quelconque dans le navigateur, ou en appelant HttpSession#invalidate() dans le serveur, ou en raison d'un bogue spécifique au serveur avec des cookies de session comme connu dans WildFly ), alors l'état de vue sérialisé n'est plus disponible en la session et l'utilisateur final obtiendront cette exception. Pour comprendre le fonctionnement de la session, Voir aussi Comment fonctionnent les servlets? L'instanciation, les sessions, les variables partagées et le multithreading .

il y a aussi une limite sur le nombre de vues que JSF stockera dans la session. Lorsque la limite est atteint, alors la moins récemment utilisée sera expiré. Voir aussi com.soleil.face.numberOfViewsInSession vs com.soleil.face.numberOfLogicalViews .

avec la méthode de sauvegarde d'état définie à client , le champ d'entrée caché javax.faces.ViewState contient à la place l'état de vue sérialisé complet, de sorte que l'utilisateur final n'obtiendra pas un ViewExpiredException lorsque la session expire. Cela peut toutefois se produire dans un environnement de cluster ("erreur: MAC n'a pas Vérifié" est symptomatique) et/ou lorsqu'il y a un délai spécifique à l'implémentation dans l'état client configuré et / ou lorsque le serveur réinvente la clé AES pendant redémarrer, voir aussi obtenir ViewExpiredException dans l'environnement clustered alors que la méthode de sauvegarde d'état est définie au client et que la session utilisateur est valide comment la résoudre.

quelle que soit la solution, assurez-vous de faire et non utilisez enableRestoreView11Compatibility . il ne restaure pas du tout l'état de vue original. Il recrée essentiellement la vue et toutes les vues scoped beans associées à partir de zéro et donc perdre toutes les données originales (état). Comme l'application se comporter d'une manière confuse ("Hey, où sont mes valeurs d'entrée..??"), c'est très mauvais pour l'expérience utilisateur. Utilisez plutôt stateless views ou <o:enableRestorableView> à la place de sorte que vous pouvez le gérer sur une vue spécifique seulement au lieu de sur toutes les vues.

quant au pourquoi JSF doit sauver l'état de vue, tête à cette réponse: pourquoi JSF sauve l'état des composants UI sur le serveur?

éviter ViewExpiredException sur la navigation de page

pour éviter ViewExpiredException lorsque , par exemple, la navigation de retour après la déconnexion lorsque l'état de sauvegarde est défini à server , il ne suffit pas de rediriger la requête POST après la déconnexion. Vous devez également donner des instructions au navigateur pour et non cache les pages JSF dynamiques, sinon le navigateur peut les afficher à partir du cache au lieu d'en Demander une nouvelle du serveur lorsque vous envoyez une demande GET sur elle (par exemple par bouton retour).

le champ caché javax.faces.ViewState de la page Mise en cache peut contenir une valeur d'ID d'état de vue qui n'est plus valide dans la session en cours. Si vous utilisez POST (command links/buttons) au lieu de GET (regular links/buttons) pour la navigation page-à-page, et cliquez sur un tel lien/bouton de commande sur la page Mise en cache, cela échouera à son tour avec un ViewExpiredException .

pour lancer une redirection Après déconnexion dans JSF 2.0, soit ajouter <redirect /> à la <navigation-case> en question (le cas échéant), ou ajouter ?faces-redirect=true à la valeur outcome .

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

ou

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

pour indiquer au navigateur de ne pas mettre en cache les pages JSF dynamiques, créer un Filter qui est mappé sur le nom de servlet du FacesServlet et ajoute les en-têtes de réponse nécessaires pour désactiver le cache du navigateur. Par exemple:

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

    // ...
}

eviter ViewExpiredException sur la page rafraîchissement 1519730920"

pour éviter ViewExpiredException lors du rafraîchissement de la page courante lorsque l'état de sauvegarde est défini à server , vous devez non seulement vous assurer que vous effectuez la navigation de page à page exclusivement par GET (liens/boutons réguliers), mais vous devez également vous assurer que vous utilisez exclusivement ajax pour soumettre les formulaires. Si vous soumettez le formulaire de manière synchrone (Non-ajax) de toute façon, alors vous feriez mieux soit de rendre la vue apatride (voir la section suivante), ou d'envoyer une redirection après POST (voir section précédente).

ayant un ViewExpiredException sur la page refresh est en configuration par défaut un cas très rare. Cela ne peut se produire que lorsque la limite de la quantité de vues que JSF stockera dans la session est atteinte. Ainsi, cela ne se produira que si vous avez défini manuellement cette limite beaucoup trop basse, ou que vous créez continuellement de nouvelles vues en arrière-plan (par exemple par un sondage mal implémenté). Voir aussi com.soleil.face.numberOfViewsInSession vs COM.soleil.face.numberOfLogicalViews . Une autre cause est d'avoir des bibliothèques JSF dupliquées dans classpath runtime en conflit les unes avec les autres. La procédure correcte pour installer JSF est décrite dans notre page wiki JSF .

Handling ViewExpiredException

quand vous voulez gérer un inévitable ViewExpiredException après une action de poste sur une page arbitraire qui a déjà été ouvert dans un onglet/fenêtre de navigateur pendant que vous êtes connecté dans un autre onglet/fenêtre, puis vous souhaitez spécifier un error-page pour que web.xml qui va à un "Votre session a expiré". Par exemple:

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

utilisez si nécessaire un en-tête meta refresh dans la page d'erreur dans le cas où vous avez l'intention de réellement rediriger plus loin à la page d'accueil ou de connexion.

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

(le 0 dans content représente le nombre de secondes avant rediriger, 0 signifie ainsi "rediriger immédiatement", vous pouvez utiliser par exemple 3 pour laisser le navigateur attendre 3 secondes avec la redirection)

noter que le traitement des exceptions durant les demandes ajax nécessite un ExceptionHandler spécial . Voir aussi délai d'expiration de Session et ViewExpiredException la manipulation sur le JSF/PrimeFaces requête ajax . Vous pouvez trouver un exemple en direct à OmniFaces FullAjaxExceptionHandler vitrine page (ce couvre également les requêtes non-ajax).

notez aussi que votre page d'erreur" générale "doit être mappée sur <error-code> de 500 au lieu de <exception-type> de par ex. java.lang.Exception ou java.lang.Throwable , sinon toutes les exceptions emballées dans ServletException comme ViewExpiredException se retrouveraient toujours sur la page d'erreur générale. Voir aussi ViewExpiredException montré en java.lang.Throwable erreur de la page web.xml .

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

Apatrides vues

une alternative complètement différente est d'exécuter des vues JSF en mode apatride. De cette façon rien de l'état JSF ne sera sauvé et les vues n'expireront jamais, mais seront reconstruites à partir de zéro sur chaque demande. Vous pouvez activer les vues apatrides en paramétrant l'attribut transient de <f:view> à true :

<f:view transient="true">

</f:view>

ainsi le champ caché javax.faces.ViewState obtiendra une valeur fixe de "stateless" en Mojarra (n'ont pas vérifié MyFaces à ce point). Notez que cette fonctionnalité était introduit dans Mojarra 2.1.19 et 2.2.0 et n'est pas disponible dans les versions plus anciennes.

la conséquence est que vous ne pouvez plus utiliser view scoped beans. Ils vont maintenant se comporter comme des demandes scoped beans. Un des inconvénients est que vous devez suivre l'état vous-même en jouant avec des entrées cachées et/ou des paramètres de requête libres. Principalement les formulaires avec champs d'entrée avec rendered , readonly ou disabled les attributs qui sont contrôlés par des événements ajax seront affectés.

notez que le <f:view> n'a pas nécessairement besoin d'être unique tout au long de la vue et/ou résident dans le modèle principal seulement. Il est aussi tout à fait légal de faire une déclaration et de l'insérer dans un client de modèle. Il "étend" fondamentalement le parent <f:view> puis. Par exemple: dans le modèle principal:

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

et dans le modèle client:

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

vous pouvez même envelopper le <f:view> dans un <c:if> pour le rendre conditionnel. Notez que cela s'applique sur les ensemble , non seulement sur le contenu imbriqué, comme le <h:form> dans l'exemple ci-dessus.

Voir aussi


sans rapport avec pour le problème concret, L'utilisation de HTTP POST pour une navigation pure page-à-page n'est pas très conviviale/SEO. Dans JSF 2.0, vous devriez vraiment préférer <h:link> ou <h:button> à la <h:commandXxx> ceux pour la navigation simple de page en page.

donc au lieu de par ex.

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

mieux faire

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

Voir aussi

313
répondu BalusC 2017-05-23 12:10:41

avez-vous essayé d'ajouter des lignes ci-dessous à votre web.xml ?

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

j'ai trouvé cela très efficace lorsque j'ai rencontré ce problème.

50
répondu Mike GH 2013-05-30 08:54:54

tout d'abord ce que vous devez faire, avant de changer web.xml est de s'assurer que votre ManagedBean implements Serializable :

@ManagedBean
@ViewScoped
public class Login implements Serializable {
}

surtout si vous utilisez MyFaces

5
répondu ZuzEL 2013-07-28 14:58:09

éviter les formes en plusieurs parties dans les Richfaces:

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

si vous utilisez Richfaces, j'ai trouvé que les requêtes ajax à l'intérieur des formulaires multipartis renvoient un nouvel ID de vue sur chaque requête.

comment déboguer:

sur chaque requête ajax un ID de vue est retourné, c'est très bien tant que L'ID de vue est toujours le même. Si vous obtenez un nouvel ID de vue sur chaque demande, alors il ya un problème et doit être résolu.

3
répondu tak3shi 2017-03-24 12:24:55

Vous petit plus ponctuellement utiliser votre propre AjaxExceptionHandler ou primefaces-extensions

mise à Jour de vos faces-config.xml

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

ajouter le code suivant dans votre jsf page

...
<pe:ajaxErrorHandler />
...
0
répondu myset 2014-01-11 10:00:50

j'ai eu cette erreur : javax.face.application.ViewExpiredException.Quand j'ai utilisé différentes requêtes, j'ai trouvé celles qui avaient le même JsessionId, même après avoir redémarré le serveur. Donc c'est dû au cache du navigateur. Fermez le navigateur et essayez, ça va marcher.

0
répondu rahul 2014-10-05 14:44:47

quand notre page est inactive pour X quantité de temps la vue expirera et jeter javax.face.application.ViewExpiredException pour empêcher que cela se produise une solution est de créer CustomViewHandler qui étend ViewHandler toutes les autres méthodes sont déléguées au Parent

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

}   

alors vous devez l'ajouter à vos visages-config.xml

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

Merci pour la réponse originale sur le ci-dessous lien: http://www.gregbugaj.com/?p=164

0
répondu user4924157 2016-05-13 06:45:29

s'il vous plaît ajouter cette ligne dans votre web.XML Il travaille pour moi

<context-param>
        <param-name>org.ajax4jsf.handleViewExpiredOnClient</param-name> 
        <param-value>true</param-value>     
    </context-param>
-2
répondu harshal 2014-06-13 15:21:05

j'ai moi-même rencontré ce problème et je me suis rendu compte que c'était à cause d'un effet secondaire d'un filtre que j'ai créé qui filtrait toutes les requêtes sur l'application. Dès que j'ai modifié le filtre pour ne sélectionner que certaines requêtes, ce problème ne s'est pas produit. Il peut être bon de vérifier de tels filtres dans votre application et de voir comment ils se comportent.

-2
répondu javshak 2014-06-25 09:40:26

j'ajoute la configuration suivante à web.xml et il a été résolu.

<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>
-3
répondu Mohamed Ali 2012-11-10 09:50:47