How do you use JSF implicit redirection with POST parameters

19,581

Solution 1

You can't redirect using POST.

When you use faces-redirect=true you are using an HTTP redirect, and what happens is: the server sends a HTTP 302 response to the browser with an URL for redirection, then the browser does a GET request on that URL.

What you can do instead is to redirect to an URL sending the id parameter via GET, going something like this:

public void goToDetails(){
    // some code
    ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext()
    String id = object.getPk().toString();
    ec.redirect(ec.getRequestContextPath() + "/details.jsf?id=" + id);
}

You may want to create an util method for this sort of thing, like Faces#redirect() of OmniFaces library.

UPDATE: As noted in the comments, it's also possible to just add the id in the return string:

public String goToDetails(){
    // some code
    String id = object.getPk().toString();
    return "details?faces-redirect=true&id=" + id;
}

Solution 2

If the backing bean behind list.jsf doesn't need to do any processing (from the example it doesn't look like it), you should link to details.jsf directly via a GET request.

You can use the <h:link> tag for this as follows:

<h:link value="details" outcome="details.jsf" >
    <f:param  name="id" value="#{listBean.object.pk}"/>
</h:link>

On your details view, you can declare that the view uses a GET parameter and bind it directly to the backing bean of that view:

<f:metadata>
    <f:viewParam name="id" value="#{detailsBean.id}" />
</f:metadata>

Additionally, you can directly validate and or convert that parameter, so your detailsBean will get an Object of the right type instead of the string-based id. If you need to do any post-processing in the detailsBean after the GET parameter is injected, you can use the preRenderView event:

<f:metadata>
    <f:viewParam name="id" value="#{detailsBean.id}" />
    <f:event type="preRenderView" listener="#{detailsBean.preRenderView()}" />
</f:metadata>

Working examples:

Also see:

Share:
19,581
Achraf
Author by

Achraf

A beginner software engineer specially in JAVA enterprise edition and some frameworks like hibernate, JSF, Spring...

Updated on June 05, 2022

Comments

  • Achraf
    Achraf almost 2 years

    In my JSF application I have two pages, list.jsf and details.jsf, and each page has its own controller with view scope. In list.jsf I have a <h:commandLink> that calls an action and pass a parameter:

    <h:commandLink value="details" action="#{listBean.goToDetails}" >
       <f:param  name="id" value="#{listBean.object.pk}"/></h:commandLink>
    

    This is the bean method:

    @ManagedBean
    @ViewScoped
    public class ListBean {
        public String goToDetails() {
            // some code
            return "details?faces-redirect=true";
        }
    }
    

    I read the parameter in the second bean like this:

    Map<String, String> params = FacesContext.getCurrentInstance()
                    .getExternalContext().getRequestParameterMap();
            this.setIdParam(params.get("id"));
    

    When I run this code, the parameter is not passed to the second bean instance. However when I change navigation to forward (without faces-redirect=true), the parameter is passed and I can see the details in details.jsf but the URL doesn't match with the current page.

    So what I want to do is to use a "jsf implicit redirection" (not a forward) with POST parameters (f:param).

  • Mike Braun
    Mike Braun over 11 years
    I'm really sorry for the down vote. The first part of your answer is really good, but I felt the second part is not optimal. You're now advising to do a PRG, while all that is needed here is a get. Even iff PRG was needed, then you could just return an outcome with the redirect directive and a parameter. Eg return "details.jsf?faces-redirect=true&id=" + id; But if you don't need PRG, just link directly. This is better for performance and direct links can be bookmarked, inspected upfront, etc.
  • Alfian Nahar
    Alfian Nahar over 11 years
    this approach is better indeed, though OP's code does have a comment implying that there is more code in the action method.
  • Alfian Nahar
    Alfian Nahar over 11 years
    @MikeBraun I agree that a simple link is much better, it's just that OP's code has a comment saying // some code in there which implied there were more stuff. Otherwise, I fully agree that a link would be best.
  • Mike Braun
    Mike Braun over 11 years
    Yes, you're right. Depending on what "some code" does, the PRG pattern would be needed indeed, but then you could use the outcome string with parameter instead of programmatically setting the redirect. I'm curious what "some code" is btw. For linking from a list to details view I've a feeling this isn't needed either, but of course only OP knows this ;)
  • Alfian Nahar
    Alfian Nahar over 11 years
    Yeah, I didn't know you could add URL parameters in the return string. Just finished testing now, and learned something new! :) Thanks
  • Achraf
    Achraf over 11 years
    Thank you for your response it is very usefull, but in my case i have to use a commandButton with action.
  • Mike Braun
    Mike Braun over 11 years
    @Achraf do you also know why you need a commandButton? If you just want a button rendered then you can also use h:button. This will fire a get request just like h:link. What special things does your action do apart from the redirect? (just curious and for helping other people which method to choose). Thanks!
  • Achraf
    Achraf over 11 years
    Thank you @MikeBraun for your interventions. In my case the action execute some code before redirecting. Explaining: I have multiple details views (details1.jsf, details2.jsf...) and each object instance must be associated with the appropriate view so in goToDetails method I have to do some logic to specify the redirection destination