X

POST-REDIRECT-GET and JSF 2.0

Guest Author

by Ed Burns

Michael Jouravlev, in his influential August 2004 article
Redirect After Post,
described a problem that many web applications present. He described the problem as follows:

All interactive programs provide two basic functions: obtaining user input and displaying the results.
Web applications implement this behavior using two HTTP methods: POST and GET respectively. This simple protocol gets
broken when an application returns a web page in response to a POST request. Peculiarities of the POST method combined with
idiosyncrasies of different browsers often lead to an unpleasant user experience and may produce an incorrect state of
the server application.

To address the problem, Jouravlev described a technique that he called POST-REDIRECT-GET, or the PRG pattern for short.
The rules of the pattern are as follows:

  • Never show pages in response to POST
  • Always load pages using GET
  • Navigate from POST to GET using REDIRECT

Previous versions of JavaServer Faces (JSF) technology violated the first
of these rules by using POST for every page navigation.
In navigating from one page to another in a JSF-enabled application, the JSF framework forwarded a POST request
through the Servlet API's RequestDispatcher.forward( ) method. This caused a new Faces page to be rendered
and returned to the browser in response to the postback request.

Indeed, most popular Java Servlet-based web frameworks, including Struts, use this approach for navigation. HTTP purists rightly
point out that this approach violates the first rule in the PRG pattern. Not only did JSF violate the first rule, but until
JavaServer Faces 2.0, it was very difficult to do it any other way.
Thanks to a JSF contribution from the Seam team at JBoss, it is now much easier to do PRG with JSF.

This Tech Tip shows how to implement the PRG pattern in JSF 2.0. The content of the tip is an adaptation of a section
on PRG and JSF 2.0 in my upcoming book, with Neil Griffin,
JavaServer Faces 2.0: The Complete Reference.

A Non-PRG Example

Let's start by examining a simple JSF 2.0 application that handles user registration. In this first example, the application
does not implement the PRG pattern. The initial page for the application is coded in file register.xhtml, as follows:



   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>A Simple JavaServer Faces Registration Application</title>
</h:head>
<h:body>
<h:form>
<h2>JSF Registration App</h2>
<h4>Registration Form</h4>
<table>
<tr>
<td>First Name:</td>
<td>
<h:inputText label="First Name"
id="fname" value="#{userBean.firstName}"
required="true"/>
<h:message for="fname" />
</td>
</tr>
<tr>
<td>Last Name:</td>
<td>
<h:inputText label="Last Name"
id="lname" value="#{userBean.lastName}"
required="true"/>
<h:message for="lname" />
</td>
</tr>
... additional table rows not shown.
</table>
<p><h:commandButton value="Register" action="confirm" /></p>
</h:form>
</h:body>
</html>




The page presents text fields for the user to enter a first name and a last name. It also displays a Register button.
When the user presses the Register button, the JSF navigation rule system looks for a page within the application whose extension
is the same as the current page and whose filename is confirm. If confirm.xhtml exists,
JSF uses the navigation components in that file to navigate to the next page. Here is the confirm.xhtml
file:



   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>A Simple JavaServer Faces Registration Application</title>
</h:head>
<h:body>
<h:form>
<h2>JSF Registration App</h2>
<h4>Registration Confirmation</h4>
<table>
<tr>
<td>First Name:</td>
<td>
<h:outputText value="First Name" value="#{userBean.firstName}"
</td>
</tr>
<tr>
<td>Last Name:</td>
<td>
<h:outputText label="Last Name" value="#{userBean.lastName}"
</td>
</tr>
... additional table rows not shown.
</table>
<p><h:commandButton value="Edit" action="register" /></p>
<p><h:commandButton value="Confirm" action="#{userBean.addConfirmedUser}" /></p>
</h:form>
</h:body>
</html>




The confirm.xhtml file includes markup for an Edit button and a Confirm button.
If the user clicks the Edit button, he or she is taken back to the register.xhtml page. If the user clicks the
Confirm button, an action is invoked. The Confirm button specifies an action method, addConfirmedUser( ), that
determines the outcome programmatically in the logic of the method. Here is the UserBean.java file, which contains
the addConfirmedUser( ) method:



   package com.jsfcompref.model;
... imports
@ManagedBean
@SessionScoped
public class UserBean {
... properties and methods
public String addConfirmedUser() {
boolean added = true; // actual application may fail to add user
FacesMessage doneMessage = null;
String outcome = null;
if (added) {
doneMessage = new FacesMessage("Successfully added new user");
outcome = "done";
} else {
doneMessage = new FacesMessage("Failed to add new user");
outcome = "register";
}
FacesContext.getCurrentInstance().addMessage(null, doneMessage);
return outcome;
}




For this simple case, addConfirmedUser( ) causes a message stating Successfully added new user
to be displayed on the page and returns "done" as the outcome.
When the addConfirmedUser( ) method returns "done" as the outcome,
it takes the user to the done.xhtml page.
This is an example of implicit navigation, a new feature in JSF 2.0.
If no matching navigation case is found after checking all available rules, the navigation handler checks to see whether
the action outcome corresponds to a view id. If a view matching the action outcome is found, an implicit navigation to
the matching view occurs. Here the outcome is "done" and the matching view is done.xhtml,
so the user is taken to the done.xhtml page. Implicit navigation saves you the effort of adding navigation rules
in the faces-config.xml file.

Here is the done.xhtml page:



   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>A Simple JavaServer Faces Registration Application</title>
</h:head>
<h:body>
<h:form>
<h2>JSF Registration App</h2>
<h4>Registration Confirmation</h4>
<h:messages />
<table>
<tr>
<td>First Name:</td>
<td>
<h:outputText value="First Name" value="#{userBean.firstName}"
</td>
</tr>
</table>
</h:form>
</h:body>
</html>





POST-REDIRECT-GET Using View Parameters

View Parameters is a simple, declarative way to map incoming request
parameter values to special components within the view. These mappings are specified using the new
<f:viewParam> component, within the new <f:metadata> section of the view. Consider the following example:



   <f:metadata>
<f:viewParam name="foo" value="#{bean.foo}"/>
</f:metadata>




This example specifies that the value of the request parameter with the name "foo" is automatically assigned
to the property at #{bean.foo}. So for a GET request as follows:



   page1.jspx?foo=bar




The value of the #{bean.foo} property will be set to bar when JSF starts processing the request.

View Parameters is similar in spirit to the page parameters feature found in JBoss Seam, but the JSF 2.0 incarnation of the feature
is tightly integrated with the core JSF specification, making the feature easier to use and more powerful.
Let’s look at another simple example.



   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>A Simple JavaServer Faces 2.0 View</title>
</h:head>
<h:body>
<h:form>
<p>First Name:< <h:inputText id="fname"
value="#{userBean.firstName}" /> </p>
<p><h:commandButton value="submit"
action="page02?faces-redirect=true&amp;includeViewParams=true" /></p>
</h:form>
</h:body>
</html>




The <h:commandButton> element has action="page02?faces-redirect=true". In the Internet standard that
defines URLs, the presence of a ? character indicates the remainder of the URL will be an & or &amp;-separated list of
name=value pairs that should be submitted to the server along with the request for the URL. This is known as a query string.
JSF borrows the meaning of the ? character here, and the meaning is exactly the same as in the Internet standard for URLs.
There are two special query strings parameters recognized by JSF when it parses the outcome on the server side.
The faces-redirectquery string tells the navigation system that this implicit navigation case must be treated as
if it were a real <navigation-case> element that includes a <redirect/> element. The other
special query string parameter, includeViewParams, tells the navigation handler to include the view parameters when
performing the navigation. But what view parameters should be included? The view parameters to be included when performing the
navigation are declared on the to-view-id page. In this case, we are using implicit navigation, so the
implicit to-view-id is page02.xhtml, shown below.



   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"><f:metadata>
<f:viewParam name="fname> value="#userBean.firstName}"/>
</f:metadata>

<h:head>
<title>A Simple JavaServer Faces 2.0 View</title>
</h:head>
<h:body>
<h:form>
<p> Hello #{userBean.firstName}.</p>
</h:form>
</h:body>
</html>




When the navigation handler encounters the matching navigation-case (implicit or explicit) that declares that view parameters should be included,
it looks at the view parameters of the from-view-id and to-view-id pages and performs a match-and-copy algorithm
to convey the view parameters to the new page. In this case, the navigation-case also requested a redirect.

Now let’s look at the registration example, this time implemented to do PRG with view parameters. The register.xhtml page looks like this:



   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"><f:metadata>
<f:viewParam name="fname" value="#{userBean.firstName}" />
<f:viewParam name="lname" value="#{userBean.lastName}" />
<f:viewParam name="sex" value="#{userBean.sex}" />
<f:viewParam name="dob" value="#{userBean.dob}">
<f:convertDateTime pattern="MM-dd-yy" />
</f:viewParam>
<f:viewParam name="mail" value="#{userBean.email}" />
<f:viewParam name="sLevel" value="#{userBean.serviceLevel}" />
</f:metadata>

<h:head>
<title>A Simple JavaServer Faces Registration Application</title>
</h:head>
<h:body>
<h:form>
<h2>JSF Registration App</h2>
<h4>Registration Form</h4>
<table>
<tr>
<td>First Name:</td>
<td>
<h:inputText label="First Name"
id="fname" value="#{userBean.firstName}"
required="true"/>
<h:message for="fname" />
</td>
</tr>
... remaining table rows omitted, they are the same as the original
</table>
<!-- The query parameters on the action attribute cause JSF to do the
POST REDIRECT GET pattern -->
<p><h:commandButton value="Register"
action="confirm?faces-redirect=true&amp;includeViewParams=true" /></p>
</h:form>
</h:body>
</html>




In the previous View Parameters example, we stated that elements only appear on the to-view-id page.
That is still true in this example, even though this is the first page the user sees. This particular application allows
the user to go back and forth between the register.xhtml page and the confirm.xhtml page. Therefore, when the
user is on the confirm.xhtml page, the to-view-id is the register.xhtml page and vice versa.
Thus, <f:viewParams> is on both pages. You will need an <f:viewParam> for every input component
on the from page that you wish to carry forward to the to page. Also note the <f:convertDateTime> within the
<f:viewParam> for the dob property. This is necessary because, when the navigation
is performed, the converter needs to be invoked to carry the value forward. If there is an explicit converter defined on the input field,
then there must also be one on the corresponding <f:viewParam>. Finally, you can see the now familiar extra query
parameters on the implicit navigation: confirm?faces-redirect=true&amp;includeViewParams=true.

Let’s examine the changes to the UserBean.java file.



   package com.jsfcompref.model;
... omits imports
@ManagedBean@RequestScoped
public class UserBean {
... additional properties omitted
public String addConfirmedUser() {
boolean added = true; // actual application may fail to add user
FacesMessage doneMessage = null;
String outcome = null;
if (added) {
doneMessage = new FacesMessage("Successfully added new user");
outcome = "done?faces-redirect=true&amp;includeViewParams=true";
} else {
doneMessage = new FacesMessage("Failed to add new user");
outcome = "register?faces-redirect=true&amp;includeViewParams=true";
}
FacesContext.getCurrentInstance().addMessage(null, doneMessage);
return outcome;
}




The only changes to this code are to make the bean be request-scoped and
to add the query parameters to the implicit navigation string. In this case, the includeViewParams=true parameter is added,
causing whatever view parameters declared on the to-view-id page to be included in the navigation.

The confirm.xhtml page follows:



   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"><f:metadata>
<f:viewParam name="fname" value="#{userBean.firstName}" />
<f:viewParam name="lname" value="#{userBean.lastName}" />
<f:viewParam name="sex" value="#{userBean.sex}" />
<f:viewParam name="dob" value="#{userBean.dob}">
<f:convertDateTime pattern="MM-dd-yy" />
</f:viewParam>
<f:viewParam name="mail" value="#{userBean.email}" />
<f:viewParam name="sLevel" value="#{userBean.serviceLevel}" />
</f:metadata>

<h:head>
<title>A Simple JavaServer Faces Registration Application</title>
</h:head>
<h:body>
<h:form>
<h2>JSF Registration App</h2>
<h4>Registration Confirmation</h4>
<table>
<tr>
<td>First Name:</td>
<td>
<h:outputText value="First Name" value="#{userBean.firstName}"
</td>
</tr>
... additional rows omitted, they are the same as the original.
</table>
<p><h:commandButton value="Edit"
action="register?faces-redirect=true&amp;includeViewParams=true" /></p>
</h:form><h:form>

<h:inputHidden value="#{userBean.firstName}" />

<h:inputHidden value="#{userBean.lastName}"/>

<h:inputHidden value="#{userBean.sex}" />

<h:inputHidden value="#{userBean.dob}">

<f:convertDateTime pattern="MM-dd-yy" />

</h:inputHidden>

<h:inputHidden value="#{userBean.email}" />

<h:inputHidden value="#{userBean.serviceLevel}" />

<p><h:commandButton value="Confirm"
action="#{userBean.addConfirmedUser}" /></p>
</h:form>

</h:body>
</html>




As in the register.xhtml page, we need the <f:metadata> section at the top of the page and the additional
query parameters on the action string. What is new here are the additional <h:form> element and
<h:inputHidden> elements, and the fact that the Confirm button has been moved into this new form.
This is necessary because we need to carry forward to the next page as regular form submit parameters the values passed to this page as
view parameters. But there are no regular input fields as there are on the register.xhtml page.
Therefore, we use hidden fields to carry the values forward. Note also the continued necessity for the
<f:convertDateTime> on the dob field.

Finally, here is the done.xhtml page:



   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"><f:metadata>
<f:viewParam name="fname" value="#{userBean.firstName}" />
<f:viewParam name="lname" value="#{userBean.lastName}" />
<f:viewParam name="sex" value="#{userBean.sex}" />
<f:viewParam name="dob" value="#{userBean.dob}">
<f:convertDateTime pattern="MM-dd-yy" />
</f:viewParam>
<f:viewParam name="email" value="#{userBean.email}" />
<f:viewParam name="sLevel" value="#{userBean.serviceLevel}" />
</f:metadata>

<h:head>
<title>A Simple JavaServer Faces Registration Application</title>
</h:head>
<h:body>
<h:form>
<h2>JSF Registration App</h2>
<h4>Registration Confirmation</h4>
<h:messages />
<table>
<tr>
<td>First Name:</td>
<td>
<h:outputText value="First Name" value="#{userBean.firstName}"
</td>
</tr>
... additional rows omitted
</table>
</h:form>
</h:body>
</html>




The only difference between this done.xhtml and the original one is the now familiar
<f:metadata> section.


Running the Sample Code

A sample application that implements PRG accompanies this tip.
These instructions use the Maven 2 software project management tool to build the sample application and then deploy it in the
GlassFish v3 Preview application server.


  1. If you haven't already done so, download a recent
    promoted build or
    nightly build of the GlassFish v3 Preview
    application server.
  2. If
    you haven't already done so, download Maven 2.
  3. Download the sample application package, PostRedirectGet.zip
  4. Extract the contents of the sample application package. You should see the folders prgViewParams,
    which contains the code for the PRG application that uses View Parameters.
  5. Create the WAR file for the PRG application by changing to the prgViewParams directory
    and entering the following Maven command:

       mvn install





    You should see the file prgViewParams.war in a newly-created target subdirectory under the
    prgViewParams directory.
  6. Start the GlassFish v3 Preview application server by entering the following command:

       <GFv3_inst>/bin/asadmin start-domain





    where <GFv3_inst> is where you installed the GlassFish v3 Preview application server.
  7. Deploy the sample application. One way to do that is to copy the prgViewParams.war file to the
    <GFv3inst>/domains/domain1/autodeploy directory.
  8. Execute the application by opening a browser and accessing the URL http://localhost:8080/prgViewParams.
    You should see the form shown in Figure 1.







    Registration Page
    Figure 1. Registration Page


     


  9. Enter information as appropriate into the form and click the Register button. You should see a page similar the one shown in
    Figure 2.







    Registering Through the Registration Page
    Figure 2. Registering Through the Registration Page


     


  10. Click the Confirm button. You should see a page similar the one shown in Figure 3.







    Confirmation Page
    Figure 3. Confirmation Page


     



Further Reading

For more information, see the following resources:


About the Author

Ed Burns is a senior staff engineer at Sun Microsystems. Ed has worked on a wide variety of client and server-side web technologies since 1994,
including NCSA Mosaic, Mozilla, the Sun Java Plugin, Jakarta Tomcat and, most recently JavaServer Faces. Ed is currently the co-spec lead for
JavaServer Faces. He is the coauthor of
JavaServer Faces: The Complete Reference
and the author of Secrets of the Rockstar
Programmers
. He is also the coauthor of the upcoming book
JavaServer Faces 2.0: The Complete Reference.
Read Ed Burns's blog.

Join the discussion

Comments ( 13 )
  • fake rolex Tuesday, November 17, 2009

    Good post!

    Thank you!


  • developerholic Saturday, November 28, 2009

    This just made me miss developing in JSF more.


  • air jordan shoes Thursday, December 3, 2009

    Thanks for your useful info, I think it's a good topic.


  • lava kafle Tuesday, December 15, 2009

    Perfect! Thanks to Ed for those hidden technological breakthroughs of JSF regarding PRG. We had been using the traditional Non-PRG methods and we always get unfathomed, unseen, unavoidable and uncontrolled nullpointer exceptions and other VIEWID exceptions from JSF.


  • Christian Louboutin Shoes Monday, December 28, 2009

    I have to say that this site is awesome and has kept me entertained on my time off from work. keep it up.


  • Balasubramanian PS Wednesday, December 30, 2009

    Superb Explanation!!! Thanks!!!


  • Sachin Jadhavar Sunday, January 3, 2010

    Thanks for additional information. It will be very useful..............


  • dizi izle Saturday, January 23, 2010

    thank you for your article...that is a nice one...i often visit this blog and i enjoy reading all posts...


  • ankur ankur Tuesday, June 8, 2010

    shows comparitive analysis in a lucid way


  • Justin Thursday, August 12, 2010

    Very Good Explanation. Quite useful information.

    Thanks


  • Justin Thursday, August 12, 2010

    Thanks for this article and good analysis.


  • Rajesh Tuesday, August 17, 2010

    Thanks for the details explanation.


  • Thang Pham Wednesday, November 3, 2010

    It is really awesome. If I can just you a question. When you click the submit button with includeViewParams=true, all the form variables are pass along to the target page, matching correctly with those that have the f:viewParam, is that right? All these are post request, correct?


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.