javax.faces.application.ViewExpiredException: la vista no se pudo restaurar

He escrito una aplicación sencilla con seguridad administrada por contenedor. El problema es cuando inicio sesión y abro otra página en la que salgo, luego vuelvo a la primera página y hago clic en cualquier enlace, etc. o renuevo la página, obtengo esta excepción. Supongo que es normal (o tal vez no :)) porque cerré la sesión y la sesión se destruye. ¿Qué debo hacer para redirigir al usuario a, por ejemplo, index.xhtml o login.xhtml y salvarlo de ver esa página / mensaje de error?

En otras palabras, ¿cómo puedo redirigir automáticamente otras páginas para indexar / iniciar sesión después de desconectarme?

Aquí está:

javax.faces.application.ViewExpiredException: viewId:/index.xhtml - View /index.xhtml could not be restred. 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) 

Introducción

La ViewExpiredException se ViewExpiredException siempre que javax.faces.STATE_SAVING_METHOD se establezca en server (valor predeterminado) y el usuario final envíe una solicitud HTTP POST en una vista a través de con , o , mientras que el estado de vista asociado ya no está disponible en la sesión.

El estado de vista se identifica como el valor de un campo de entrada oculto javax.faces.ViewState de . Con el método de ahorro de estado configurado en server , este contiene solo el ID de estado de vista que hace referencia a un estado de vista serializada en la sesión. Entonces, cuando la sesión expira por algún motivo (ya sea que haya agotado el tiempo en el servidor o cliente, o la cookie de sesión ya no se mantiene por algún motivo en el navegador, o llamando a HttpSession#invalidate() en el servidor o debido a un servidor específico error con las cookies de sesión como se conoce en WildFly ), entonces el estado de vista serializada ya no está disponible en la sesión y el usuario final obtendrá esta excepción. Para comprender el funcionamiento de la sesión, consulte también ¿Cómo funcionan los servlets? Instanciación, sesiones, variables compartidas y multihebra .

También hay un límite en la cantidad de vistas que JSF almacenará en la sesión. Cuando se alcanza el límite, la vista utilizada menos recientemente expirará. Consulte también com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews .

Con el método de ahorro de estado establecido en el client , el campo de entrada oculto javax.faces.ViewState contiene en su lugar el estado de vista serializado completo, por lo que el usuario final no obtendrá una ViewExpiredException cuando ViewExpiredException la sesión. Sin embargo, todavía puede suceder en un entorno de clúster (“ERROR: MAC no verificó” es sintomático) y / o cuando hay un tiempo de espera específico de la implementación en el estado del cliente configurado y / o cuando el servidor vuelve a generar la clave AES durante el reinicio , consulte también Obtener ViewExpiredException en un entorno agrupado, mientras que el método de ahorro de estado se establece en el cliente y la sesión del usuario es válida para resolverlo.

Independientemente de la solución, asegúrese de no utilizar enableRestoreView11Compatibility . no restaura en absoluto el estado original de la vista. Básicamente, recrea la vista y todos los beans de ámbito de vista asociado desde cero y, por lo tanto, pierde todos los datos originales (estado). Como la aplicación se comportará de una manera confusa (“Oye, ¿dónde están mis valores de entrada … ??”), esto es muy malo para la experiencia del usuario. Mejor use vistas sin estado o lugar para que pueda administrarlo en una vista específica solamente en lugar de en todas las vistas.

En cuanto a por qué JSF necesita guardar el estado de la vista, diríjase a esta respuesta: ¿Por qué JSF guarda el estado de los componentes de la interfaz de usuario en el servidor?

Evitar ViewExpiredException en la navegación de la página

Para evitar ViewExpiredException cuando, por ejemplo, navega hacia atrás después del cierre de sesión cuando el ahorro de estado se establece en el server , solo redirigir la solicitud POST después del cierre de sesión no es suficiente. También debe indicar al navegador que no almacene en caché las páginas JSF dinámicas; de lo contrario, el navegador puede mostrarlas desde la memoria caché en lugar de solicitar una nueva desde el servidor cuando envíe una solicitud GET (por ejemplo, mediante el botón Atrás).

El campo oculto javax.faces.ViewState de la página almacenada en caché puede contener un valor de ID de estado de vista que ya no es válido en la sesión actual. Si utiliza (ab) POST (enlaces / botones de comando) en lugar de GET (enlaces / botones regulares) para la navegación de página a página, y hace clic en dicho enlace o botón de comando en la página almacenada en caché, esto a su vez fallar con una ViewExpiredException .

Para activar un redireccionamiento después de cerrar la sesión en JSF 2.0, agregue al en cuestión (si existe), o agregue ?faces-redirect=true al valor de outcome .

  

o

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

Para indicar al navegador que no almacene en caché las páginas JSF dinámicas, cree un Filter que esté asignado al nombre del servlet de FacesServlet y agregue los encabezados de respuesta necesarios para deshabilitar el caché del navegador. P.ej

 @WebFilter(servletNames={"Faces Servlet"}) // Must match  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); } // ... } 

Evitar ViewExpiredException en la página de actualización

Para evitar ViewExpiredException al actualizar la página actual cuando el ahorro de estado está configurado como server , no solo debe asegurarse de realizar la navegación de página a página exclusivamente mediante GET (enlaces / botones regulares), sino que también necesita asegúrese de utilizar exclusivamente ajax para enviar los formularios. Si envía el formulario de forma sincrónica (no ajax) de todos modos, será mejor que la vista sea sin estado (consulte la sección posterior) o que envíe una redirección después del POST (consulte la sección anterior).

Tener una ViewExpiredException en la actualización de la página está en configuración predeterminada, un caso muy raro. Solo puede suceder cuando se alcanza el límite en la cantidad de vistas que almacenará JSF en la sesión. Por lo tanto, solo ocurrirá cuando haya configurado manualmente ese límite demasiado bajo, o que esté continuamente creando nuevas vistas en el fondo (por ejemplo, mediante una encuesta mal implementada). Consulte también com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews . Otra causa es que las bibliotecas JSF duplicadas en classpath en tiempo de ejecución entran en conflicto entre sí. El procedimiento correcto para instalar JSF se describe en nuestra página wiki de JSF .

Manejo de ViewExpiredException

Cuando desee manejar una inevitable ViewExpiredException después de una acción POST en una página arbitraria que ya fue abierta en alguna pestaña / ventana del navegador mientras está desconectado en otra pestaña / ventana, entonces desea especificar una error-page de error-page para eso en web.xml que va a una página “Su sesión ha expirado”. P.ej

  javax.faces.application.ViewExpiredException /WEB-INF/errorpages/expired.xhtml  

Use, si es necesario, un encabezado de actualización de meta en la página de error en caso de que tenga la intención de redireccionar aún más a la página de inicio o de inicio de sesión.

    Session expired    

Session expired

You will be redirected to login page

Click here if redirect didn't work or when you're impatient.

(el 0 en el content representa la cantidad de segundos antes de redirigir, 0 significa “redirigir inmediatamente”, puede usar, por ejemplo, 3 para que el navegador espere 3 segundos con la redirección)

Tenga en cuenta que el manejo de excepciones durante las solicitudes ajax requiere un ExceptionHandler especial. Consulte también el tiempo de espera de la sesión y el manejo de ViewExpiredException en la solicitud JSF / PrimeFaces ajax . Puede encontrar un ejemplo en vivo en la página de presentación de OmniFaces FullAjaxExceptionHandler (esto también cubre solicitudes que no son Ajax).

También tenga en cuenta que su página de error “general” debe asignarse a de 500 lugar de a un de, por ejemplo, java.lang.Exception o java.lang.Throwable , de lo contrario, todas las excepciones envueltas en ServletException como ViewExpiredException aún terminaría en la página de error general. Consulte también ViewExpiredException que se muestra en java.lang.Throwable error-page en web.xml .

  500 /WEB-INF/errorpages/general.xhtml  

Vistas sin estado

Una alternativa completamente diferente es ejecutar vistas JSF en modo sin estado. De esta forma, nada del estado de JSF se guardará y las vistas nunca caducarán, sino que se reconstruirán desde cero en cada solicitud. Puede activar las vistas sin estado estableciendo el atributo transient de en true :

   

De esta forma, el campo oculto de javax.faces.ViewState obtendrá un valor fijo de "stateless" en Mojarra (no ha marcado MyFaces en este punto). Tenga en cuenta que esta característica se introdujo en Mojarra 2.1.19 y 2.2.0 y no está disponible en versiones anteriores.

La consecuencia es que ya no puede usar ver los granos de ámbito. Ahora se comportarán como beans de ámbito de solicitud. Una de las desventajas es que tiene que rastrear el estado usted mismo jugando con entradas ocultas y / o parámetros de solicitud sueltos. Principalmente aquellos formularios con campos de entrada con atributos rendered , de readonly o disabled que están controlados por eventos ajax se verán afectados.

Tenga en cuenta que no necesariamente tiene que ser único en toda la vista y / o residir solo en la plantilla maestra. También es completamente legítimo redeclararlo y anidarlo en una plantilla cliente. Básicamente, “extiende” el padre continuación. Por ejemplo, en la plantilla maestra:

    

y en plantilla cliente:

   ...   

Incluso puede ajustar en a para hacerlo condicional. Tenga en cuenta que se aplicaría en toda la vista, no solo en los contenidos nesteds, como el en el ejemplo anterior.

Ver también

  • ViewExpiredException se muestra en java.lang.Throwable error-page en web.xml
  • Compruebe si existe sesión JSF
  • Tiempo de espera de la sesión y manejo de ViewExpiredException en la solicitud JSF / PrimeFaces ajax

Sin relación con el problema concreto, el uso de HTTP POST para la navegación pura de página a página no es muy amigable para el usuario / SEO. En JSF 2.0, realmente debería preferir o sobre los para navegación simple de página a página.

Entonces, en lugar de, por ejemplo

      

mejor hacer

    

Ver también

  • ¿Cuándo debería usar h: outputLink en lugar de h: commandLink?
  • Diferencia entre h: button y h: commandButton
  • ¿Cómo navegar en JSF? Cómo hacer que la URL refleje la página actual (y no la anterior)

¿Has intentado agregar líneas a continuación en tu web.xml ?

  com.sun.faces.enableRestoreView11Compatibility true  

Encontré esto muy efectivo cuando encontré este problema.

Lo primero que debe hacer antes de cambiar a web.xml es asegurarse de que ManagedBean implements Serializable :

 @ManagedBean @ViewScoped public class Login implements Serializable { } 

Especialmente si usas MyFaces

Evite formularios multiparte en Richfaces:

    

Si está utilizando Richfaces, he encontrado que las solicitudes ajax dentro de formularios de varias partes devuelven una nueva ID de vista en cada solicitud.

Cómo depurar:

En cada solicitud de ajax, se devuelve una ID de vista, eso está bien siempre que la ID de vista sea siempre la misma. Si obtiene una nueva ID de vista en cada solicitud, entonces hay un problema y debe corregirse.

Usted podría usar su propio AjaxExceptionHandler o Primefaces-Extensions

Actualiza tu faces-config.xml

 ...  org.primefaces.extensions.component.ajaxerrorhandler.AjaxExceptionHandlerFactory  ... 

Agregue el siguiente código en su página jsf

 ...  ... 

Recibí este error: javax.faces.application.ViewExpiredException. Cuando usaba diferentes solicitudes, encontré las que tenían el mismo JsessionId, incluso después de reiniciar el servidor. Esto se debe a la memoria caché del navegador. Solo cierre el navegador e intente, funcionará.

Cuando nuestra página está inactiva durante un período de tiempo x, la vista caducará y lanzará javax.faces.application.ViewExpiredException para evitar que esto suceda. Una solución es crear CustomViewHandler que amplíe ViewHandler y anule el método restreView. Todos los demás métodos se delegan al Padre

 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 restreView(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.restreView(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; } } 

Luego debes agregarlo a tus faces-config.xml

  com.demo.CustomViewHandler  

Gracias por la respuesta original en el siguiente enlace: http://www.gregbugaj.com/?p=164

Por favor agregue esta línea en su web.xml. Funciona para mí

  org.ajax4jsf.handleViewExpiredOnClient true  

Me encontré con este problema por mi cuenta y me di cuenta de que se debía a un efecto secundario de un filtro que creé y que filtraba todas las solicitudes en la aplicación. Tan pronto como modifiqué el filtro para seleccionar solo ciertas solicitudes, este problema no ocurrió. Tal vez sea bueno verificar esos filtros en su aplicación y ver cómo se comportan.

Agregué la siguiente configuración a web.xml y se resolvió.

  com.sun.faces.numberOfViewsInSession 500   com.sun.faces.numberOfLogicalViews 500