Understanding alwaysRedirectOnPause

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.

Redirect after submit

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:

Double Submit Warning
Figure 1. A browser displaying a warning to avoid a double submit.

Figure 2 below illustrates the double submit problem:

Double Submit
Figure 2. 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:

Redirect after Submit
Figure 3. Redirecting after a submit.

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.

Request processing lifecycle

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:

  1. First request: submit data - EVENT PROCESSING PHASE
    When the first request comes in, Spring Web Flow will prepare the RequestContext for the request. This is a new request so a brand new request scope is created. Since event ("next") is being signaled, flash scope will be cleared. The flow now does the following processing:
    1. The flow execution resumes in the "displayIntro" view state and transitions to the "displayForm" view state because of the "next" event being signaled.
    2. The flow enters the "displayForm" view state, executing any entry actions (none in this example). The render actions will not be executed since Spring Web Flow knows that it should only render the view after a redirect.
    3. The view state issues a redirect response. This redirect is a so called FlowExecutionRedirect, refreshing a paused flow execution.
    The event processing phase is shown graphically in figure 4.
  2. Flow execution pauses; REDIRECT issued
    Spring Web Flow will pause and store a flow execution in between requests using a flow execution repository. As a result, the contents of flash scope and flow scope are serialized out.
Event Processing Phase
Figure 4. The event processing phase: a browser signaling the "next" event against flow execution "12345".
Event processing results in a redirect response.
  1. Second request: flow execution refresh - RENDER PHASE
    The second request is completely independent of the first request, so it receives it's own, initially empty request scope. The request is just refreshing the flow execution and not signaling an event, so flash scope remains as it was and is not cleared.
    1. The paused flow execution refreshes.
    2. The "displayForm" view state refreshes, executing the render actions since a view will actually be rendered during this request. In this example the "setupForm" method of the FormAction is executed, preparing the form for display.
    3. The "form" view is rendered using all relevant property editors since a "setupForm" was done earlier in this request.
    The processing done during the render phase is illustrated in figure 5.
  2. Flow execution pauses; response rendered
    Spring Web Flow will once again pause and store the flow execution in a repository.
Render Phase
Figure 5. The render phase: a browser performing the redirect to "refresh" flow execution "12345".
The refresh results in an HTML response to the browser.

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.

Pitfalls

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.

Manual redirects

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>

Discussion

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.

© Copyright Ervacon 2011. All Rights Reserved. Contact us.
All trademarks and copyrights on this page are owned by their respective owners.