Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

faces-redirect and the back button cause other links to work incorrectly

I have a question surrounding faces navigation.

So I have a page that takes a request parameter to load a specific user. This page displays a list of commandLink that, when clicked, redirect to another page using implicit navigation. The user is loaded by calling a method in a "preRenderView".

The page that we redirect to also takes a request parameter to determine which case to load. The page is also using a preRenderView.

Both pages have beans that are view scoped.

This all works awesome for the FIRST link that is pressed. The first page redirects to the new URL and the case loads as expected.

HOWEVER, if I click the browser's back button and then click another link the page DOESN'T redirect. It refreshes (instead) and, obviously, doesn't shop the case.

After the refresh, I can click a link and it will redirect properly.

Now, all of this works totally fine when the first page's bean is kept in the session but I don't want to abuse the session state and I don't think it should be required for me to keep this data in the session.

I know I can fix this by having the page automatically reload when I click the back button (because the view will be recreated) but I'm not sure if that's the correct solution either. (nor am I sure how I would force this)

Does anyone have any suggestions? This seems like a fairly common use case but I couldn't really find any examples.

Thanks!

like image 206
jjross Avatar asked Oct 20 '11 16:10

jjross


1 Answers

Basically, you need to tell the browser to not cache the dynamically generated JSF pages while having view state saving method set to (default) server. The view state is tracked by a <input type="hidden" name="javax.faces.ViewState"> field in the form of the generated JSF page with the view state identifier as input value. When you submit a page and navigate to a different page, then the view state is trashed in the server side and do not exist anymore. Getting the page back from the browser cache would still give you that old view state identifier as value of the hidden input. Submitting that form won't work at all as no view state in the server side can be found.

You want to get a fresh new page straight from the server instead of from the browser cache. In order to tell the browser to do that, create a Filter like this:

@WebFilter(servletNames={"Faces Servlet"})
public class NoCacheFilter implements Filter {

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

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

        chain.doFilter(request, response);
    }

    // ...
}

This way the back button will send a fullworthy HTTP request which should recreate the view state and result in a page with a form with the proper view state hidden field value.

like image 89
BalusC Avatar answered Nov 15 '22 09:11

BalusC