This note details the now defunct Ervacon Spring Web Flow system and not the official Spring Web Flow sub project of the Spring Framework.
We advice all users of Ervacon Spring Web Flow to migrate to the current release of Spring Web Flow. Visit the Spring Web Flow Portal for more information, download links and support options.
This note describes techniques you can use to improve browser back button handling in your Spring web flows based applications.By Erwin Vervaet (firstname.lastname@example.org)
Properly dealing with use of the browser Back button is a typical problem in web applications. When a user uses the back button, there is a danger that he / she will see stale data or data that is out of sync with the state on the server. This problem cannot be solved in general since the back button accesses the history maintained by the browser and the HTTP/1.1 specification does not mandate a refresh in this case (quoted from section 13.13 of RFC 2616):
User agents often have history mechanisms, such as "Back" buttons and history lists, which can be used to redisplay an entity retrieved earlier in a session.
History mechanisms and caches are different. In particular history mechanisms SHOULD NOT try to show a semantically transparent view of the current state of a resource. Rather, a history mechanism is meant to show exactly what the user saw at the time when the resource was retrieved.
By default, an expiration time does not apply to history mechanisms. If the entity is still in storage, a history mechanism SHOULD display it even if the entity has expired, unless the user has specifically configured the agent to refresh expired history documents.
Most of the popular browsers (e.g. Internet Explorer) do not strictly follow the specs and also refresh the data when using the back button, provided the appropriate HTTP headers have been set to disable any caching. Read Redirect After Post for a detailed discussion of this topic and a clever way of avoiding many related problems.
When you're using Spring web flows, there is an additional problem to deal with. Web flows provide a powerful way to define and handle the navigation in your web application. However, web flows are stateful: they keep track of where you are in your application. As a result use of the browser back button can create situations where the server side flow state is out of sync with the view displayed in the browser.
The techniques discussed in the following sections improve the ability to deal with back button use in applications where navigation is controlled with a web flow. They do not, and cannot, solve all possible related problems, e.g. double submits.
Web flows are typically used to model actual web applications, and not so much web sites where a user can freely navigate through the pages. When you use a web flow, you assume that the application really has a flow, which somewhat contradicts free navigation.
This being said, it is often acceptable, especially for intranet applications, to inform the user he / she cannot use the browser back button and should stick to the controls (links, buttons, ...) offered by the application. It's hard to technically disable the back button, so most of the time the web application will just run in a popup window with no browser buttons being displayed. In an intranet setting, it could also be possible to roll out a specialized browser that really prevents use of the back button.
When avoiding the back button is not possible or acceptable, you can also make sure your web flows behave properly, even when the back button is being used.
This is easy when you are only using stand alone flows: flows that do not use other flows as sub flows or are used by other flows as sub flows. In this case it suffices to explicitly add the "_currentState" parameter in each request you send to the flow controller. When you do this, the controller will always know what state to use, which will result in correct navigation in all situations. Here's an example of how this can be done in the PhoneBook sample application:
<FORM name="searchForm" action="search"> <INPUT type="hidden" name="_flowId" value="<%=request.getAttribute("flowId") %>"> <INPUT type="hidden" name="_currentState" value="criteria"> <INPUT type="hidden" name="_event" value="search">
By adding the "_currentState" hidden field to the search form, we are explicitly asking the search web flow to trigger the "search" event in the "criteria" state. This technique solves web flow navigation problems caused by back button use when there is only one flow. When you're using sub flows, you could still have situations that can cause problems.
When you're using sub flows, it is not enough to just make sure you're always explicitly specifying the current state. The reason is that there could be confusion as to what flow should execute the request: the current (sub) flow or any of its parent flows?
When a web flow encounters navigation problems, e.g. because it cannot find a state or transition, it will generate a NavigationException. To make back button use possible, even when using sub flows, the Spring web flows system includes a Spring handler exception resolver that will try to resolve the navigation exceptions. This exception resolver is implemented in the class com.ervacon.springframework.web.servlet.mvc.webflow.SubFlowBackNavigationExceptionResolver. To enable this class, you just have to define it as a bean in the application context of the dispatcher servlet, like so:
<bean id="backNavigation" class="com.ervacon.springframework.web.servlet.mvc.webflow.SubFlowBackNavigationExceptionResolver"> </bean>
Using this exception resolver, you get fairly robust web flow navigation when using the browser back button. However, this is not a 100% full proof solution, so make sure you appropriately test your application!
This note explained the problems that can be caused by using the browser back button in a web application in general, and when using Spring web flows in particular. Since most Spring web flow based applications should inherently avoid the use of the back button, the first solution is to ask the user not to use it. When that's not an option, you have some other techniques to improve the behaviour of web flows even when the back button is used: always explicitly specify the current state and use the SubFlowBackNavigationExceptionResolver. However, these are not perfect solutions, so if your web application absolutely has to support browser back button use and browser refreshes, Spring web flows is not for you.