Force JSF to refresh page / view / form when opened via link or back button
Asked Answered
G

3

6

I have a JSF page which posts data to an external page. The data is loaded from a JSF managed bean which generates a unique ID in the post data.

I have an issue where a user clicks on a checkout button then navigates back to the same page and presses the checkout button again. The post data has not updated. Moreover, the bean is not invoked at all. Is there anyway to force JSF to reload the page and the form data?

<form action="#{checkoutBean.externalUrl}" method="post"
    id="payForm" name="payForm">
       <input type="hidden" value="#{checkoutBean.uniqueID}" />
       <input type="submit" value="Proceed to Checkout" />
</form>
Gooseberry answered 31/12, 2012 at 12:5 Comment(0)
V
11

That page is likely being loaded from browser cache. This is essentially harmless, but indeed confusing to the enduser, because s/he incorrectly thinks that it's really coming from the server. You can easily confirm this by looking at the HTTP traffic monitor in browser's web developer toolset (press F12 in Chrome/FireFox23+/IE9+ and check "Network" section).

You basically need to tell the browser to not cache (dynamic) JSF pages. This way the browser will actually request the server for the page (and hereby triggering proper creation/initialization of managed beans and so forth) instead of showing the previously requested one from its cache.

Generally, this is to be done with a simple servlet filter like follows:

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

    // ...
}

Where /app/* is the example URL pattern on which you'd like to turn off the browser cache. You can if necessary map it on /*, *.xhtml or even on servletNames={"Faces Servlet"}.

If you happen to use JSF utility library OmniFaces, then you can use its builtin CacheControlFilter by just adding the following entry to web.xml (which demonstrates a direct mapping on FacesServlet, meaning that every dynamic JSF page won't be cached):

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

See also the showcase.

Vowelize answered 31/12, 2012 at 14:26 Comment(6)
Lets say I don't want to cache my 3rd page. My bean would have got created/initialized when my 1st first page would have rendered. If depending on my 1st or 2nd page, data is populated in 3rd page and from here it gets posted. And if we instruct the browser for proper creation/initialization of bean at this point then my data set during rendering of 1st and 2nd page will be lost. How to overcome this BalusC?Tripodic
@OscarSmith: The other "solution" may work, but it is not the correct way.Vowelize
why not? It allows specific control for different pages, and has no obvious drawbacks.Schizothymia
I am not sure, but shouldn't it be chain.doFilter(request, response); instead of chain.doFilter(req, res);?Cobra
@Hendrik: nope, it are exactly the same values. Just a different reference. You know, Java is OO. It doesn't copy values when referenced differently. See also e.g. stackoverflow.com/a/3330884Vowelize
@Vowelize Thank you! That was an embarrassing comment of mine.Cobra
G
6

I found a solution that works for JSF without having to create a servlet-filter. Just put the line below to your .xhtml page.

<f:event type="preRenderView" listener="#{facesContext.externalContext.response.setHeader('Cache-Control', 'no-cache, no-store')}" />
Grip answered 7/4, 2016 at 11:45 Comment(1)
This answer is much cleaner. Is there a way to clean it up with newer JSF features?Schizothymia
F
0

You can easily refresh your page by adding this line of code at the end of the method your button will call when clicked: Faces.refresh();

Foamflower answered 28/7, 2023 at 15:0 Comment(2)
This requires you to add OmniFaces.Schonfeld
Thank you, Jasper de Vries. Adding Omnifaces is mandatory. which gives access to the Faces class and then the refresh() method.Foamflower

© 2022 - 2024 — McMap. All rights reserved.