Post Redirect Get Design Pattern
Post redirect get design pattern solves the problem of duplicate form/data submission in a POST request (assuming you know what the difference between a POST and GET request is). What is duplicate form submission? Most of you must be aware, but just to make it clear, duplicate form/data submission is sending same form/data again to a server which is already sent. The scenario we are talking here is submission unintentionally. Now, how do you resubmit unintentionally? It is by using the browser’s feature – ‘Refresh’. You press this button and a whole lot of data goes to the server again and the server takes a defined action.
Where could this create a problem?
When the form submission results in a server side operation that changes the state of the application, persistent store or any external system, then this can become a problem. Alright, let us take a simple example to understand this. Suppose you are placing a purchase order on an internet site, when you submit the last page, you find that the confirmation page hasn’t loaded properly. You take the best commonly available action and press the browser refresh button – bang! A confirmation which is fully technical – “Do you want to repost the data?” appears. We mundanely say yes, and press the affirmation button. When you check your credit card statement there are two transactions on same day. This is the place where the repost of data becomes a serious problem. Similar scenarios can be found with application states and interactions with external systems.
Next problem is that you cannot book mark the page.
Where this cannot be a problem?
There are cases where the repost may not cause a problem. Your application is a plain vanilla data display application. Few selects on database, may be with a couple of permission checks, and a set of data is dumped on screen to the user. User refreshes the screen, browser reposts the data, and after following the same process, the data is again presented on screen. This does not cause any harm to the application. If you are worried about hundreds of user pressing refresh and shooting the load on application then you are thinking of a very remote uncommon scenario. If you are worried about the pop up confirmation message to repost data, then you can take a call on this after reading this article further.
How do you fix it?
Simple! Implement this design pattern. Change the response in such a way that, when you resubmit the request, it is a GET request. This is done by redirecting the response generated by the POST request using HTTP response code 303 (sometimes 302), and rendering a different page using a GET request. Now this result page does not cause any problems on resubmission of form. Here are the steps to be followed to implement this design pattern with MVC pattern.
- Controller receives request and processes to get data to be returned.
- Controller puts the data in a session object and redirects (HTTP 303/302 response) to an intermediate page/controller (this can be common for all pages).
- Intermediate controller reads the data from the session and renders the page.
What are Consequences of This Fix?
So far it all appears good, but it is not just a fix to repost form, there are lot more changes to the default browser request-response processing. Here are the changes:
- Only explicit actions on pages will result in a POST request
- Actions from browser refresh and back button will result in a GET request
- Browser refresh button does not behave normally i.e. it will not repost the data
- Browser back button will not go back (i.e. to -1 history) with a post request
Now that there are no POST requests from browser on refresh and back button, there will be no special handling of these two buttons by the browser.
- Now on refresh button click, browser will not generate pop up confirmation to repost data.
- On click of back button, browser will take you to the intermediate page, and if the session data is not managed correctly then it may not show the previous page correctly. It may sometimes appear blank. Weird isn’t it? But session management is your business.
- You want your page to expire on the click of the back button, and you have added the following HTML META tags to not cache and expire the page. Generally, browser uses these tags in case of POST requests, now we don’t have POST request, instead there is GET request. Hence your page may not expire on the press of the back button although you have these tags. Yes! There will be a problem with the browser’s back button.
What are the alternatives?
The above problem gets more difficult, when you are hosting a secured site. The rules of game are altogether different. Let’s try to find an alternative. Actually, let us look at the problem again. The problem is stopping duplicate post of requests. Why can’t we just check the request before processing the request, if it is duplicate request instead of modifying browser’s default behavior? Synchronization Token pattern can be used to fix this problem.
What is Synchronization Token Pattern?
- Add a hidden variable in your pages/forms called as token.
- When page is rendered, set some value to this token, using a counter too.
- Put next counter value in session.
- When the form is submitted, check if the token is equal or less than the value in session, if yes then take appropriate action.
Thus you haven’t changed the default behavior of browser, and also handled duplicate submission. Back button should also respect the HTML META tags.
Struts and Spring MVC support this approach.
- Struts gives org.apache.struts.taglib.html.TOKEN to be used for this business.
- With Spring MVC, there is a synchronizeOnSession property to be configured on AbstractController (actually applicable to any controller). (Just to add on, the HTML META tags mentioned above can be replaced by properties (cacheSeconds, useExpiresHeader, useCacheHeader) in Spring MVC).
Finally, post redirect get is meant to be for converting POST request response into a GET request response and it comes with alteration of browser’s default behavior. You have to choose the correct implementation based on your requirements, whether to go for a complete post redirect get or just stop the duplicate post of the request.
If you enjoyed this post, please consider to leave a comment or subscribe to the feed and get future articles delivered to your feed reader.
