What are the possible ways to prevent form from double posting in ASP.NET MVC?
During a log inspection of my ASP.NET MVC application I discovered that some u开发者_C百科sers managed to post same form twice. I tried to reproduce it but I failed. Anyway I'd like to prevent users from posting same data twice. Is there any canonical solution for that in ASP.NET MVC or I have to develop my own custom solution?
I case of custom solution I can imagine client side and server side solution. Can you recommend me anything in either case?
The usual approach is to disable the submit button immediately after clicking it.
Something like this with jQuery should work:
<script type="text/javascript">
$("#myform").submit(function()
{ $("#submit-button").attr("disabled", "disabled"); });
</script>
Now after reading closely your question and seeing that you use ASP.NET MVC....
The double post is probably caused because users refresh the page after posting it. If you simply return a view from your post action:
[AcceptVerbs (HttpVerbs.Pos)]
ActionResult ProcessUserPost ()
{
/*...*/
return View ();
}
Then the browser will attempt to recreate the page by performing the same steps, meaning submitting the remembered form again. Some browsers will display a warning message (FireFox), the others will just do it quietly (looking at Opera). In order to avoid this you need to assure the page the user is getting is the result of a GET request not POST.
Basically, you need to perform a redirect instead of returning a view. This will instruct the browser to fetch a page again.
[AcceptVerbs (HttpVerbs.Pos)]
ActionResult ProcessUserPost ()
{
/*...*/
return RedirectToAction ("DisplayTheForm");
}
This way refreshing the page won't cause a new POST request being sent.
Of course, this will not prevent your users clicking the back button to return to the form page to submit it again. But that situation should be handled in your business logic, check for double submission of the same order by the same user of whatever it is that you are doing.
I can think of three ways to handle this.
- Have a double post be non-destructive. That is, when possible make it so that a repost simply refreshes the data in a way that renders it still valid. This isn't really possible with a post that creates something -- say a credit card transaction -- but should suffice for an update method when you are basically setting new properties.
- Prevent double posts using a one-time nonce that is only good for one action. Include a hidden nonce on the page that you track and mark as used once the first post has been seen. Check this nonce when you get it to make sure that it hasn't been used before and, if it has, show an error message rather than taking an action.
- Prevent destructive actions by checking uniqueness constraints on the posted data. This is really a variant on (2) in that your nonce is basically some unique value in the actual data. This works for some creation scenarios, i.e., you can't create a duplicate user with the same email address, but fails when their is no unique field in the data. An example of the latter might be a purchase transaction where the user might buy the same item more than one time.
One technique I have used to prevent a double post is using a server side and client side key that changes with each post. It works like this:
Generate a unique (random) key on the server and place it in the session and also in a hidden field.
When the user posts back the first time compare the key in the hidden field to the key in the session, and, if they match, accept the input and then change or remove the key from the session and update the hidden field as well.
If the user manages to click submit twice the second post will fail because the hidden field in the HTML will no longer match the session variable until the page has been refreshed.
Two step process
Disable buttons first.
This will prevent users from submitting the form twice while browser waits for server response. But you should be careful with this one. When user hits submit, you will have to first validate your form (if you use client validation) and when it's valid, imediatelly disable buttons using this code.
$(":submit, :button").attr("disabled", true);
Use one of the following redirect action results in your controller
This will prevent users from hitting F5 after the form has been processes to resend POST data. This is called POST-REDIRECT-GET pattern.
RedirectToAction();
RedirectToRoute();
Redirect(); // redirects to URL
EDIT
As you've said in comments, your page already uses P-R-G pattern. In this case I'd make sure to implement first step.
精彩评论