22

Please correct me if I'm wrong, but I'm thinking of all of my non-AJAX submits should use the Post/Redirect/Get (PRG) way, since GET should be used to refresh/query data, and in my case, the application pages I can think of really only do update on the data and then refresh the page, so I think PRG fits here.

I believe I can do this using the faces-config.way, where I make use of the <redirect/>, or I can use the return "myview.xhtml?faces-redirect=true";

Now the question is ..

Is there any way I can configure this globally that for non-AJAX calls/submits, automatically make use of faces-redirect=true, so that my source is as simple as this:

return "myview";
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Bertie
  • 17,277
  • 45
  • 129
  • 182

1 Answers1

28

You could do this with a custom ConfigurableNavigationHandler. Here's a kickoff example:

package com.example;

import java.util.Map;
import java.util.Set;

import javax.faces.application.ConfigurableNavigationHandler;
import javax.faces.application.NavigationCase;
import javax.faces.application.NavigationHandler;
import javax.faces.context.FacesContext;

public class RedirectNavigationHandler extends ConfigurableNavigationHandler {

    private NavigationHandler parent;

    public RedirectNavigationHandler(NavigationHandler parent) {
        this.parent = parent;
    }

    @Override
    public void handleNavigation(FacesContext context, String from, String outcome) {
        if (!outcome.endsWith("?faces-redirect=true")) {
            outcome += "?faces-redirect=true";
        }

        parent.handleNavigation(context, from, outcome);        
    }

    @Override
    public NavigationCase getNavigationCase(FacesContext context, String fromAction, String outcome) {
        if (parent instanceof ConfigurableNavigationHandler) {
            return ((ConfigurableNavigationHandler) parent).getNavigationCase(context, fromAction, outcome);
        } else {
            return null;
        }
    }

    @Override
    public Map<String, Set<NavigationCase>> getNavigationCases() {
        if (parent instanceof ConfigurableNavigationHandler) {
            return ((ConfigurableNavigationHandler) parent).getNavigationCases();
        } else {
            return null;
        }
    }

}

Register it as follows in faces-config.xml:

<application>
    <navigation-handler>com.example.RedirectNavigationHandler</navigation-handler>
</application>  
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Is possible to make this handler work when action method return void (stay on the same page) `` ? – marioosh Aug 08 '11 at 07:34
  • 2
    Hi, Albert. You can add code to handler and check if the request is an AJAX request getting a PartialViewContext from FaceContext and then invoking PartialViewContext.isAJAXRequest http://download.oracle.com/javaee/6/api/javax/faces/context/PartialViewContext.html#isAjaxRequest%28%29. – victor herrera Aug 14 '11 at 05:52
  • hi BalusC: Is there a reason why you `extends ConfigurableNavigationHandler` instead of `extends NavigationHandler`? What does `ConfigurableNavigationHandler` give you more that `NavigationHandler` does not? – Thang Pham Jun 22 '12 at 01:12
  • @Thang: this is required since JSF 2.0. See also introductory text of the class' javadoc http://docs.oracle.com/javaee/6/api/javax/faces/application/ConfigurableNavigationHandler.html – BalusC Jun 22 '12 at 01:20
  • Thank you BalusC: For some reasons, when I debug, it does not get inside `getNavigationCase(...)` or `getNavigationCases()`. Is it because I did not specify any navigation cases inside my `faces-config.xml`. My `commandButton` just return the view id. – Thang Pham Jun 22 '12 at 01:41
  • Can this be anyway applied in application with Faces Flow? problem is that `outcome` can be a name of a FlowNode and then it won't cause any effect (redirect will be stripped from outcome later). – T.G Oct 07 '14 at 08:35
  • Is there a good way to keep RequestScoped beans going over the redirect? – mattalxndr Aug 27 '15 at 19:19