浏览器返回+ viewscope bean

时间:2011-11-09 08:40:54

标签: jsf-2 back-button

问题是什么: 单击浏览器后退按钮时会发生什么 - >打开一个页面,其viewscoped-managedbean已被销毁 - >使用grid-record-selections从该页面的commandButton提交请求?

我的期望: 重新创建关联的viewscope-managebean,接收网格记录选择,并处理它们,就像从不涉及浏览器后退按钮一样。

我的体验: 不会重新创建关联的viewscope-managebean,也不会接收网格记录选择。必须重新输入URL,或点击浏览器后退按钮后重新输入F5才能再次正常工作。

所以这是成功方案,所有bean都是视图编组bean:

  1. 获取page1.xhtml - > page1Bean在@PostConstruct
  2. 中创建,查询数据等
  3. 检查/选择数据表中的多条记录,单击“处理按钮”
  4. page1Bean的进程方法将选定的记录存储在flash对象中,并重定向到page2.xhtml
  5. page1Bean已销毁,page2Bean已创建,并且在preRenderView侦听器方法中,从flash对象中提取所选记录并处理它们
  6. 点击“转到主页面”commandButton重定向到page1.xhtml,并且page2Bean被破坏,page1Bean再次创建
  7. 来自no 2 - 5的
  8. 循环仍然可行
  9. 现在,这是涉及浏览器后退按钮的错误情况(从#6开始发生的不同事情):

    1. 获取page1.xhtml - > page1Bean在@PostConstruct
    2. 中创建,查询数据等
    3. 检查/选择数据表中的多条记录,单击“处理按钮”
    4. page1Bean的进程方法将选定的记录存储在flash对象中,并重定向到page2.xhtml
    5. page1Bean已销毁,page2Bean已创建,并且在preRenderView侦听器方法中,从flash对象中提取所选记录并处理它们
    6. 点击浏览器后退按钮page2Bean未销毁,未创建page1Bean
    7. 检查/选择数据表中的多条记录,单击“处理按钮”
    8. page1Bean方法执行(奇怪,因为page1Bean应该被销毁),但是看不到所做的记录选择,并且重定向到page2.xhtml
    9. page1Bean未被销毁(没有日志输出),page2Bean未创建(因为它没有被销毁),像往常一样执行preRenderView监听器,但这次,flash对象中没有选定的记录
    10. 是否可以使用带浏览器后退按钮的viewscope-beans获得正常体验(就像没有浏览器后退按钮一样)?

      这是我的依赖:

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

      请分享您的想法!

2 个答案:

答案 0 :(得分:16)

浏览器似乎从其缓存服务页面而不是向服务器发送完整的HTTP GET请求,而您将JSF状态保存方法设置为server(这是默认值)。

有两种方法可以解决这个问题:

  1. 告诉浏览器不要缓存动态JSF页面。您可以借助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);
    }
    

    将过滤器映射到FacesServlet或其相同的网址格式。

  2. 将JSF状态保存方法设置为client,以便整个视图状态存储在表单的隐藏字段中,而不是存储在服务器端的会话中。

    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>client</param-value>
    </context-param>
    
  3. 过滤方式更可取。

答案 1 :(得分:0)

禁用页面浏览器缓存的缺点是,如果用户使用浏览器返回导航到上一页,则会看到浏览器错误页面。 所以另一种解决方案是使用javascript来识别页面是来自服务器还是来自浏览器缓存:

首先创建一个简单的支持bean,它提供唯一的id(在我的情况下是当前的系统时间):

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

现在,您可以测试是否从服务器或浏览器提供页面,并在当前页面来自浏览器缓存时重定向用户。请参阅jsf页面中的以下javascript代码,该代码不应由浏览器缓存:

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

脚本也可以放在facelet中,使jsf代码更加干净。