首页 > 解决方案 > 使用 org.omnifaces.cdi.ViewScoped 时,我的 javax.servlet.Filter 在单个页面视图上同时接收到 GET 和 POST

问题描述

我用于记录页面视图的 servletfilter 正在接收带有单个页面请求的 GET 和 POST。我将其追溯到在页面支持 bean 上使用 Omnifaces ViewScope。

@Named
@org.omnifaces.cdi.ViewScoped

我碰巧注意到我的日志文件中带有精确时间戳的双页浏览量。使用以下简化版本进行调试,在单个页面请求上,doFilter 执行两次,第一次是 GET,URL 是我正在浏览的 URL。然后 doFilter 再次执行,它是一个 POST 并且 URL 是我来自的页面。如果我刷新页面,我会看到一个 GET,然后是一个 POST 到同一页面。如果我使用javax.faces.view.ViewScoped,只有 GET 请求进来。

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    System.out.println(new java.util.Date() + " " + ((HttpServletRequest) request).getMethod() + " " + httpRequest.getServletPath());
    chain.doFilter(request, response);
}

例如,如果我正在查看 http://localhost:8080/myApp/page1.xhtml 并将 url 更改为 page2.xhtml

过滤器会写出

Wed Aug 26 12:17:04 EDT 2020 GET /page2.xhtml
Wed Aug 26 12:17:04 EDT 2020 POST /page1.xhtml

也许是设计使然?. 但我只想记录用户正在浏览的页面,而不是他们来自的页面。是不是很简单?:

    if(((HttpServletRequest) request).getMethod().equalsIgnoreCase("GET")){
      //Write to actual log file
    }

还是我使用omnifaces viewscope错了?

标签: jsfomnifaces

解决方案


这确实是设计使然。前一页上的 POST 基本上发送一个页面已卸载的信号,因此背后的逻辑@ViewScoped知道它必须立即销毁 JSF 视图状态和物理 bean。

另请参阅文档

...这个 CDI 视图范围注释将保证@PreDestroy在浏览器卸载时也会调​​用带注释的方法。这个技巧是由navigator.sendBeacon. 对于不支持的浏览器navigator.sendBeacon,它将回退到同步 XHR 请求。

当您想在过滤器中检测它们时,您可以使用ViewScopeManager#isUnloadRequest().

if (!ViewScopeManager.isUnloadRequest(request)) {
    // Write to actual log file.
}

chain.doFilter(request, response); // Ensure that this just continues!

诚然,这确实记录得不够清楚,我会在下一个版本中记住这一点。


推荐阅读