强制JSF在通过链接或后退按钮打开时刷新页面/视图/表单

6

我有一个JSF页面,它将数据发布到外部页面。 数据是从JSF托管的bean中加载的,该bean在发布数据中生成唯一ID。

我的问题是,当用户单击结帐按钮然后返回相同的页面并再次按下结帐按钮时,发布的数据未更新。此外,bean根本没有被调用。是否有任何方法强制JSF重新加载页面和表单数据?

<form action="#{checkoutBean.externalUrl}" method="post"
    id="payForm" name="payForm">
       <input type="hidden" value="#{checkoutBean.uniqueID}" />
       <input type="submit" value="Proceed to Checkout" />
</form>
3个回答

11

该页面 likely 是从浏览器缓存加载的。这基本上是无害的,但确实会让最终用户感到困惑,因为他/她错误地认为它确实来自服务器。您可以通过查看浏览器的 web 开发工具集中的 HTTP 流量监视器(在 Chrome/Firefox23+/IE9+ 中按 F12 并检查 "Network" 部分)轻松确认此操作。

您基本上需要告诉浏览器不要缓存(动态)JSF 页面。这样,浏览器将实际向服务器请求页面(从而触发受管 bean 的正确创建/初始化),而不是从其缓存中显示先前请求的页面。

通常,这可以通过一个简单的servlet 过滤器 来完成,如下所示:

@WebFilter("/app/*")
public class NoCacheFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        if (!request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)
            response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
            response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
            response.setDateHeader("Expires", 0); // Proxies.
        }

        chain.doFilter(req, res);
    }

    // ...
}

这里的/app/*是您想要关闭浏览器缓存的示例URL模式。如有必要,您可以将其映射到/**.xhtml或甚至servletNames={"Faces Servlet"}

如果您使用JSF实用程序库OmniFaces,则可以使用其内置的CacheControlFilter,只需将以下条目添加到web.xml中即可(该示例直接映射到FacesServlet,这意味着每个动态JSF页面都不会被缓存):

<filter>
    <filter-name>noCache</filter-name>
    <filter-class>org.omnifaces.filter.CacheControlFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>noCache</filter-name>
    <servlet-name>facesServlet</servlet-name>
</filter-mapping>

请参阅演示


假设我不想缓存第三页。我的bean将在渲染第一页时被创建/初始化。如果取决于我的第一页或第二页,在第三页中填充数据,并从此处进行发布。如果我们在此时指示浏览器正确地创建/初始化bean,那么在渲染第一页和第二页时设置的数据将丢失。如何克服这个问题,BalusC? - Vikas V
@OscarSmith:另一种“解决方案”可能有效,但这不是正确的方法。 - BalusC
为什么不呢?它可以针对不同的页面进行特定控制,而且没有明显的缺点。 - Oscar Smith
我不确定,但是应该是chain.doFilter(request, response);而不是chain.doFilter(req, res);,对吗? - Hendrik
@Hendrik:不是的,它们确实是完全相同的值。只是一个不同的引用。你知道,Java是面向对象的。当以不同的方式引用时,它不会复制值。也可以参考例如 https://dev59.com/oXA75IYBdhLWcg3wYH3I#3330884 - BalusC
@BalusC 感谢您!那是我一个尴尬的评论。 - Hendrik

6
我发现了一种适用于JSF的解决方案,不需要创建servlet-filter。只需将以下行添加到您的.xhtml页面即可。
<f:event type="preRenderView" listener="#{facesContext.externalContext.response.setHeader('Cache-Control', 'no-cache, no-store')}" />

这个答案更加简洁。有没有一种方法可以使用更新的JSF功能来清理它? - Oscar Smith

0
你可以在按钮点击时调用的方法的末尾添加这行代码来轻松刷新页面:Faces.refresh();

这需要您添加OmniFaces。 - Jasper de Vries
谢谢,Jasper de Vries。添加Omnifaces是强制性的,它可以访问Faces类和refresh()方法。 - Codeur-14

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接