|
| Home | Products | Subversion | About | Contact | |
A lot of questions on the Spring Web Flow forum are related to Spring Web Flow's alwaysRedirectOnPause setting. This tip explains that feature in detail.11/02/2007 - By Erwin Vervaet (erwin@ervacon.com) and Keith Donald - Spring Web Flow version 1.0
Spring Web Flow tries to promote best practices. For instance, unit test support is part of the core framework. Another best practice is the so called POST-REDIRECT-GET idiom. Let's begin by investigating that pattern.
The two most important methods of the HTTP protocol are GET and POST. GET was designed to retrieve data from the server without causing side-effects. The POST method on the other hand allows you to post (send, submit) data to the server, causing side effects like storage of the posted data in the database. The POST-REDIRECT-GET idiom states that you should always issue a redirect after a POST, causing a subsequent GET request. HTTP does not enforce the strict semantics of the GET and POST methods and many web applications (ab)use GET requests to submit data to the server. So we can reformulate the POST-REDIRECT-GET idiom in a more general way as redirect after submit: always issue a redirect after a submit (using either GET or POST).
Redirecting after every submit is a best practice because it solves the double submit problem. Without this pattern, when a user submits data to the server the web application will process it and render a response all in one request. However, if the user clicks the browser Refresh or Back / Forward buttons the data will need to be resubmitted, causing a double submit. Most modern browsers know about this problem and will display a warning when a user tries to resubmit data, as shown in figure 1:

Figure 2 below illustrates the double submit problem:

If you issue a client side redirect ( HTTP result codes 303 and 302) as the response to a submit request, you avoid the double submit problem. The client side redirect instructs the browser to go and search for the results elsewhere. A browser will never add the submit (POST) request to its history and will only take the subsequent redirected request (GET) into account. This second request can be safely refreshed since it's an indempotent GET request. The user is now free to use the browser Refresh and Back / Forward buttons without causing any side effects in the application. Figure 3 illustrates this situation:

Spring Web Flow's alwaysRedirectOnPause behavior automatically applies redirect after submit to every request, without developer intervention. By default alwaysRedirectOnPause is switched on: every time the flow pauses to render a view, Spring Web Flow will issue a redirect causing the view to be rendered in the subsequent GET request.
In most situations alwaysRedirectOnPause is a Good Thing. However, it does make things a bit more complicated since every interaction with the server is now composed of two requests instead of just one. To understand some of the consequences of this you must understand how a typical signal event and display a view interaction works in Spring Web Flow. Take the following flow snippet as an example:
<view-state id="displayIntro" view="intro"> <transition on="next" to="displayForm"/> </view-state> <view-state id="displayForm" view="form"> <render-actions> <action bean="formAction" method="setupForm"/> </render-actions> <transition on="submit" to="displayResults"> <action bean="formAction" method="bindAndValidate"/> </transition> </view-state>
Now suppose you're looking at the "intro" view displayed by the "displayIntro" view state. Signalling the "next" event will kick of the following process:


If you now press the browser Refresh button, you would just resubmit the second request, refreshing the paused flow execution and rendering the "form" view again after all render actions have been re-executed. Note that the entry actions of the "displayForm" state are not re-executed since the flow is already in the "displayForm" state.
As you can see the processing is composed of two phases: an event processing phase during the first request and a render phase in a second request. The render phase can be repeated any number of times.
The more complex request processing lifecycle caused by alwaysRedirectOnPause has some consequences you should be aware of.
If you keep the above pitfalls and guidelines in mind, using alwaysRedirectOnPause should be easy.
If alwaysRedirectOnPause is not appropriate in your situation or if you want more control over when a redirect is issued, you can turn off alwaysRedirectOnPause using an execution attribute of the flow executor.
<flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="continuation"> <flow:execution-attributes> <flow:alwaysRedirectOnPause value="false"/> </flow:execution-attributes> </flow:executor>
With alwaysRedirectOnPause turned off, applying the redirect after submit idiom is still very easy using the "redirect:" prefix in the view attribute of your view states. This will cause the view to be rendered after a client side redirect. Here is an example:
<view-state id="displaySomeView" view="redirect:someView"> <transition on="someEvent" to="someState" /> </view-state>
Discuss this tip with fellow Spring Web Flow users on the Spring Web Flow forum.
For more Spring Web Flow related information, visit the Ervacon Spring Web Flow Portal and read Working with Spring Web Flow.