Question

Say, you are submitting a form, which affects your database (adding records/ deleting them/ updating them) and this is how your request looks like:

POST /application/action=update

Now, say, you are done with your update, so you would like to take the user to the home page.

Response.sendRedirect /application/action=home

This works wonderfully well. User is sent a redirect after POST, so even if the user tries to refresh the page by hitting F5, you are good. However, this will not work if you did this:

requestDispatcher.forward(/application/action=home)

Given that there is a scenario where you have to display different kinds of error / success messages after you are done with your update, you are most likely doing a forward after POST. In such a scenario, how do you avoid update actions from happening twice?

I find it rather amusing that many secure sites (banks) / payment gateways tend to inform the user by placing text on screen, such as "Please don't press back / refresh buttons".

Is there no better way to handling this? Other than requesting the user not to press these buttons? When I last checked, there was something called the 'Vertical Response Cache'. A Filter that would identify uniqueness of your request in a session and tries to send a cached response if the request is duplicate. Are there any simpler ways to solving this classic problem?

Here is a link to the vertical response cache solution I was talking about: http://www.fingo.info/en/articles/_1.html. I am, However, not sure as to how well this really works.

Was it helpful?

Solution

One thought that I've had is to embed a unique ID (probably a random string) as a hidden form field in the form that is being POST-submitted. The ID string can be put in the database as a "transaction ID". Now, when you go to update the database, first check whether there's an existing record with the submitted transaction ID, and if so, assume it's a duplicate and don't change the database.

Of course, as I said, this is just a thought. I don't know what methods are actually used in practice. (I suspect that a lot of less-critical sites just ignore the problem and hope their users will be smart... a losing proposition if I ever saw one ;-)

EDIT: as pointed out in the comments, storing transaction IDs in the database might take up a lot of space, but if that's an issue, you could keep an in-memory cache of all transaction IDs processed in the last 5 minutes/1 hour/1 day/whatever. That should work unless you're up against a determined hacker...

OTHER TIPS

Yes, I believe that you should redirect after a POST, with the exception of API requests. Without doing this not only do you have to worry about getting duplicate POSTs when the user uses the back button, but the browser will also give the user annoying dialogs when they try to use the back button.

Response.sendRedirect works in practice, but tecnically speaking this is sending the wrong HTTP response code for this purpose. sendRedirect sends a 302, but the correct code to use to transform a POST into a GET is 303. (most browsers will treat a 302 just like a 303 if they get it in response to a POST, however)

In general you want the redirect to send the user to whatever view will display the effect of their change. For example, if they edit a widget, they should be redirected to the view of that widget. If they delete a widget, they should be redirected to the view that the widget would have appeared in when it existed (perhaps the widget list).

Sometimes it's nice to have a status message to further drive home the fact that an action occurred. A simple way to do this is to have a common parameter to your views that, when set, will display an action completed message. eg:

/widget?id=12345&msg=Widget+modified.

Here the "msg" parameter contains the message "Widget modified". The one downside to this approach is that it may be possible for malicious sites to give your users confusing/misleading messages. eg:

/account?msg=Foo+Corp.+hates+you.

If you're really worried about this you could include an expiring signature for the message as an additional parameter. If the signature is invalid or has expired, simply don't display the message.

The best solution to solve the problem of showing status messages to the users after a POST to GET redirect is to use user sessions.

How

Add attributes to user session with value as set of messages to be displayed. for eg.

userSession.put("success_messages", new HashSet<String>(){"Success", "Check your account balance"});
userSession.put("warning_messages", new HashSet<String>(){"Your account balance is low. Recharge immediately"});

And have a filter which scans the user session for these particular attributes and outputs the messages. The filter should delete the attributes after reading once, as the status messages are generally displayed only once.

I find it rather amusing that many secure sites (banks) / payment gateways tend to inform the user by placing text on screen, such as "Please don't press back / refresh buttons".

some people find its better to "disable all Back, Refresh event on this critical pages"; I'm not sure if this is good or not.

But your addressed solution "vertical response cache" sounds nice

Its a little non-obvious but:

  • create a keyed-object in the user session.
  • the value is a Request + java Future for the result
  • return immediately with a client-side redirect.
  • while the client-side redirect is being handled, have a worker thread work on producing the answer.

So by the time the client browser completes the redirect, getting the new page's images, etc... the results are waiting for the user.

The alternative is to make the user painfully aware of how long the database is taking.

Security Update (2011 Jan 24 ) :

The key is vulnerable to attack since it is part of the response to the client, so

  1. Generate a random key
  2. Use user's session id as a salt to create a SHA-1
  3. Store both the random key and the SHA-1 in the database with (, ) as the primary key. (no separate indexing on the RANDOMKEY.
  4. Use both RANDOMKEY and the SHA-1 as the db lookup.
  5. Do not store the Session Id (avoid privacy issues with being able to corollate many entries to the same user)
  6. Expire the results after 2-3 days. ( Allows a daily batch job to do the clean up and avoids creating problems for user sessions that are semi-long lasting )

This method requires any hacker to know both the session id and the random key.

This approach may seem overkill, but a redirect-hardened mechanism can be used for situations like password resets.

If you are working with java server side scripting and also using struts 2 then you refer this link which talks about on using token .

http://www.xinotes.org/notes/note/369/

A token should be generated and kept in session for the initial page render, when the request is submitted along with the token for the first time , in struts action run a thread with thread name as the token id and run the logic whatever the client has requested for , when client submit again the same request, check whether the thread is still running(thread.getcurrentthread().interrupted) if still running then send a client redirect 503.

Please look at the ExecuteAndWaitInterceptor of struts 2code, the logic of this combined with token will help out fast click

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top