CallumVass
CallumVass

Reputation: 11448

Prevent double form submissions

I have an ASP.NET MVC 3 application which has a post action called Create:

[HttpPost]
public virtual ActionResult Create(Issues issues)
{
    if (ModelState.IsValid)
    {
        context.Issues.Add(issues);
        context.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(issues);
}

If I double click on the submit button by mistake, it'll Create 2x issues. Is there a way of preventing this, without the use of javascript and disabling the button, I thought the whole use of using a RedirectToAction was to prevent this, by using the Post/Redirect/Get design pattern?

Upvotes: 6

Views: 9911

Answers (5)

CShark
CShark

Reputation: 2221

The PRG pattern will not prevent this, as the P action in it takes time (which is usually the case) and the user can submit the form again (via click or browser refresh), which will cause the PRG pattern to "fail".

Note that malicious users can also bypass all of your client side measures by running multiple http posts in quick succession.

A solution to all of the above is to check for duplicate submissions on server side using the following method, as described by myself, here.

For your convenience, I quote:

"If you make use of a hidden anti-forgery token in your form (as you should), you can cache the anti-forgery token on first submit and remove the token from cache if required, or expire the cached entry after set amount of time.

You will then be able to check with each request against the cache whether the specific form has been submitted and reject it if it has."

Upvotes: 0

gdoron
gdoron

Reputation: 150253

Easy solution!

Disable the submit button when it's clicked. The End.

$('submitButtonId').click(function(){
    $(this).prop('disabled', true);
    $(this).attr('disabled', 'disabled'); // for jQuery versions < 1.6.
});

Upvotes: 6

Chris Gessler
Chris Gessler

Reputation: 23113

The pattern you mentioned (Post/Redirect/Get design pattern) prevents page refreshes from double posting because the last action is a GET, not the POST.

If you want to prevent double clicks from double posting, you could:

1. Disable the button after click
2. Maintain a unique index on 'Issues' and look for duplicates
3. Maintain something in session that gives you an indication of a double post

And there maybe a few more ways... I think disabling the button is probably the easiest because you're redirecting to another page anyway.

Upvotes: 4

Shadow Wizard
Shadow Wizard

Reputation: 66389

Most simple way I can think of is use the Session:

if (ModelState.IsValid)
{
    DateTime now = DateTime.Now;
    if (Session["LastAdd"] == null || (now - (DateTime)Session["LastAdd"]).TotalMilliseconds > 1000)
    {
        //first time, or more than a second passed since last addition
        context.Issues.Add(issues);
        context.SaveChanges();
        Session["LastAdd"] = now;
    }
    return RedirectToAction("Index");
}

Upvotes: 2

Vladimir Perevalov
Vladimir Perevalov

Reputation: 4159

You can generate an IssueId when you show the form to the user, then check that you don't already have an issue with such id in your Create method and, for example, skip such requests.

Upvotes: 1

Related Questions