jsf-2 back-button

jsf 2 - navegador atrás+viewscope beans



jsf-2 back-button (2)

¿Cuál es el problema ? ¿Qué sucede cuando se hace clic en el botón Atrás del navegador?> Se abre una página cuyo beans administrado por viewscoped ya está destruido -> se envía una solicitud desde un comando desde esa página con selecciones de registro de cuadrícula?

Lo que espero : el viewscope-managebean asociado se vuelve a crear, recibe las selecciones de registro de cuadrícula y se ocupa de ellas como si el botón de retroceso del navegador nunca estuviera involucrado.

Lo que experimento : el viewscope-managebean asociado NO se vuelve a crear, no recibe las selecciones de registro de cuadrícula. Debe volver a ingresar la URL, o F5 después de hacer clic en el botón de retroceso del navegador para que funcione correctamente nuevamente.

Así que aquí está el escenario de éxito , todos los beans son beans viewcoped:

  1. GET page1.xhtml -> page1Bean creado, consulta de datos, etc. en @PostConstruct
  2. marque / seleccione varios registros de una tabla de datos, haga clic en el botón de proceso
  3. El método de proceso de page1Bean almacena los registros seleccionados en el objeto flash y lo redirecciona a la página 2.xhtml
  4. page1Bean destruido, page2Bean created, y en el método de escucha preRenderView, recupera los registros seleccionados del objeto flash y los trata
  5. haga clic en el botón de comando "ir a la página principal" para redirigir a page1.xhtml, y page2Bean destruido, page1 Bean creado de nuevo
  6. el bucle desde el 2 al 5 sigue siendo factible

Ahora, este es el escenario erróneo que involucra el botón de retroceso del navegador (diferentes cosas pasando a partir del n. ° 6):

  1. GET page1.xhtml -> page1Bean creado, consulta de datos, etc. en @PostConstruct
  2. marque / seleccione varios registros de una tabla de datos, haga clic en el botón de proceso
  3. El método de proceso de page1Bean almacena los registros seleccionados en el objeto flash y lo redirecciona a la página 2.xhtml
  4. page1Bean destruido, page2Bean created, y en el método de escucha preRenderView, recupera los registros seleccionados del objeto flash y los trata
  5. haga clic en el botón Atrás del navegador página 2Bean no se destruye, page1Bean no se crea
  6. marque / seleccione varios registros de una tabla de datos, haga clic en el botón de proceso
  7. se ejecuta el método page1Bean (extraño, porque el page1Bean debería haberse destruido), pero no puede ver las selecciones de registros realizadas, y se redirige a page2.xhtml
  8. page1Bean no se destruye (sin salida de registro), page2Bean no se crea (ya que no se destruyó), ejecuta el oyente preRenderView como de costumbre, pero esta vez, no hay registros seleccionados en el objeto flash

¿Es posible tener la experiencia normal (como sin el botón de retroceso del navegador) con viewscope-beans con el botón de retroceso del navegador?

Aquí está mi dependencia:

<dependency> <groupId>org.glassfish</groupId> <artifactId>javax.faces</artifactId> <version>2.1.3</version> <scope>compile</scope> </dependency>

Por favor comparte tus ideas!


La desventaja de deshabilitar el caché del navegador de una página es que el usuario verá la página de error de un navegador si usa el buscador para navegar a la página anterior. Entonces, otra solución es identificar si la página proviene del servidor o de la memoria caché del navegador usando javascript:

Primero crea un bean de respaldo simple que sirve una identificación única (en mi caso, la hora actual del sistema):

@Named("browserCacheController") @RequestScoped public class BrowserCacheController implements Serializable { private static final long serialVersionUID = 1L; /** * Returns a unique increasing id for each request * @return */ public long getCacheID() { return System.currentTimeMillis(); } }

Entonces ahora puede probar si una página se sirve desde el servidor o el navegador y redirigir al usuario si la página actual proviene de la memoria caché del navegador. Vea el siguiente código de javascript ubicado en una página jsf que el navegador no debe almacenar en caché:

<script type="text/javascript"> // check for latestCacheID if (!isValidCacheID(#{browserCacheController.cacheID})) { //redirect to some page document.location="#{facesContext.externalContext.requestContextPath}/index.jsf"; } // test cacheID if it comes from the server.... function isValidCacheID(currentCacheID) { if (!(''localStorage'' in window && window[''localStorage''] !== null)) return true; // old browsers not supported var latestCacheID=localStorage.getItem("org.imixs.latestCacheID"); if (latestCacheID!=null && currentCacheID<=latestCacheID) { return false; // this was a cached browser page! } // set new id localStorage.setItem("org.imixs.latestCacheID", currentCacheID); return true; } </script>

El script también se puede colocar en facelet para hacer que el código jsf sea más limpio.


El navegador parece haber servido a la página desde su caché en lugar de enviar una solicitud HTTP GET completa al servidor, mientras que tiene el método de almacenamiento de estado JSF configurado en el server (que es el predeterminado).

Hay 2 formas de resolver este problema:

  1. Indique al navegador que no almacene en caché las páginas dinámicas JSF. Puedes hacer esto con la ayuda de un filtro .

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

    Asigne el filtro en FacesServlet o su mismo patrón de URL.

  2. Establezca el método de ahorro de estado JSF en el cliente, de modo que todo el estado de la vista se almacene en un campo oculto del formulario en lugar de en la sesión en el lado del servidor.

    <context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>client</param-value> </context-param>

La forma de filtro es preferible.