Retaining GET request query string parameters on JSF form submit

Solution 1:

Your concrete problem is caused because a JSF <h:form> submits by default to the current request URL without any query string. Look closer at the generated HTML output, you'll see

<form action="/app/agreement.xhtml" ...>

You'd thus explicitly need to include those request parameters yourself. There are several ways to solve this. If you weren't sending a redirect, then you could just add them as hidden inputs to the JSF form.

<h:form>
    <input type="hidden" name="site" value="#{param.site}" />
    <input type="hidden" name="site" value="#{param.serviceId}" />
    ...
</h:form>

Only, those parameters won't reappear in URL in browser's address bar. This isn't a problem if you're only using using ajax on the same page. The <h:inputHidden> is by the way not suitable as it will confusingly lose its value when a conversion or validation error occurs on the form.

In order to get them to reappear in URL, you need <f:viewParam> and includeViewParams. In order to get includeViewParams to work, you need to declare the following in both the source page agreement.xhtml ...

<f:metadata>
    <f:viewParam name="site" value="#{agreement.site}" />
    <f:viewParam name="serviceId" value="#{agreement.serviceId}" />
</f:metadata>

... and the target page generated.xhtml:

<f:metadata>
    <f:viewParam name="site" value="#{generated.site}" />
    <f:viewParam name="serviceId" value="#{generated.serviceId}" />
</f:metadata>

Now you can send a redirect including the view parameters as follows:

public String generateMethod() {
    // ...

    return "generated?faces-redirect=true&includeViewParams=true";
}

Do note that the bean should be @ViewScoped in order to keep those parameters alive between opening the page with the form and submitting the form, also on validation errors. Otherwise, when sticking to a @RequestScoped bean, you should be retaining them as <f:param> in the command components:

<h:commandButton ...>
    <f:param name="site" value="#{generated.site}" />
    <f:param name="serviceId" value="#{generated.serviceId}" />
</h:commandButton>

There's no way to set them for <f:ajax> inside input components, your bean should then really be @ViewScoped.


Alternatively, if you happen to use JSF utility library OmniFaces already, then you could also just replace the <h:form> by <o:form> as follows (see also showcase example):

<o:form>

That's basically all. This will generate a <form action> with current query string included.

<form action="/app/agreement.xhtml?site=US&serviceId=AABBCC" ...>

Those request parameters are then just available in the request parameter map of the form submit. You don't need additional metadata/viewparams and you also don't need to send a redirect and your bean can be kept @RequestScoped, if necessary.

public String generateMethod() {
    // ...

    return "generated";
}

See also:

  • What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
  • How to navigate in JSF? How to make URL reflect current page (and not previous one)

Solution 2:

Your generateMethod would have to return

return "generated?site=US&amp;serviceId=AABBCC&amp;faces-redirect=true";

You can even replace &amp; with & but escape it in your xhtml.

In your generated.xhtml you can catch the parameters that are being passed with <f:viewParam> like this

<f:metadata>
    <f:viewParam name="site" value="#{yourBean.site}"/><!--Make sure you have a setter-->
    <f:viewParam name="serviceId" value="#{yourBean.serviceId}"/><!--Make sure you have a setter-
</f:metadata>
<h:head>