Retaining GET request query string parameters on JSF form submit

http-request-parameters, jsf, jsf-2, navigation

Solution

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="serviceId" 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)

Problem

I have 3 pages: - `main.xhtml` - `agreement.xhtml` - `generated.xhtml` The `agreement.xhtml` needs two parameters to load correctly: `serviceId` and `site`. So, a normal url looks like this: `/app/agreement.xhtml?site=US&serviceId=AABBCC`. I have this button on `agreement.xhtml` ``` <h:form> <h:commandButton value="Generate License File" action="#{agreement.generateMethod}" /> </h:form> ``` The `@RequestScoped` bean `#{agreement}` has this method: ``` public String generateMethod(){ ....... return "generated"; } ``` I need that, on click, the `generateMethod()` method is executed, and after it's done, the user is redirected to the `generated.xhtml` page. What's happening is that, on click, the page browser sends the user to `/app/agreement.xhtml` and, since it's not sending the parameters `site` and `serviceId`, it crashes. I tried making the `generateMethod()` return a `"generated?faces-redirect=true"`, but still nothing. Any ideas?

Original source

Related problems