Saturday, July 5, 2008

Maintaining form values on browser "back" in a multi-page forms

I have lately been working on a sign-up page for Solaris. The page had to be developed in PHP, which is not my favourite language - I prefer Java. But as it is, what both Solaris' hosting provider and the university uses, I at least could develop on the university and then deploy to our hosting provider. However, I am still not very confident at programming PHP. I did however manage to overcome most of the difficulties I encountered - but one remains and it really bugs me!

I have designed the page as a multi-page form with four steps. In the two first the user enters information, then there is a review step to let the user verify that the entered information is what they intended, and finally they get a confirmation on submitting of the info.
On the back-end I use a PHP session to maintain state between the various pages. And data is submitted as POST requests.

Now, the problem arises when the user uses her browsers "back" button to get back to page 2. How it manifests itself varies a bit between browsers. Firefox will simply display the page, as it looked when you last visited - without any of the entered data. And Internet Explorer will tell you that the page is expired. In both cases can the missing data be fixed by reloading the page. Requiring the users to do that is obviously not really a solution. (Both POST and GET would work.)

Even a quite intensive Google-session did not turn up anything of any real value, but did however provide a bit of insight. For instance - I am not the only one, who is annoyed by this. One of the more common approaches I encountered was to try and disable the back button by various means. E.g. open the form in a window without tool bars and similar trickery - none of which really did anything to remedy the problem, but would encumber the web page and thus the user experience. And as a computer scientist it would really hurt my sense of professional pride.

But I suppose a little analysis is in order, as it might not be entirely obvious what is going on here.
The problem stems from the semantics of a POST request - it changes the state of the server, e.g. submitting a purchase order. This should obviously not be done more than once, unless you press the "submit" button again or similar. (There might be cases where performing the same action multiple times in a row would make sense.) So what you really would want is the page to be re-requested by GET or something similar.

This leads me to first attempt to solve this, I suppose you could call it the "POST-Redirect-GET" approach. Here the main idea is that if the submitted data is accepted (passes validation, etc.) then the browser is redirected to the next page of the form, which it should request via GET.

It took a bit to come up with this solution, but putting it to use is really straight forward.
if($_SERVER['REQUEST_METHOD'] == 'POST') {
//Handle the data - e.g.

$error = !validatePost();
if($error)
//Respond with error page - with or without Redirect
else {
header('Location: ' . getTheURL());
exit(0);
}
}
So the first thing I do, is to check whether this is a POST request. I find this a handy way to figure out when I need to handle new submitted data, and facilitate this approach.
Them comes the mandatory validation part. And finally the redirect.

That's it. There is really nothing fancy to it...

I my case was it rather pointless to send a redirect in response to invalid data, but other circumstances might warrant it.

I have afterwards found numerous sites explaining this idea. My favourite example is this piece from TheServerSide.com, which has many additional points about why this is a good design pattern.

No comments: