I want to prevent users submitting forms multiple times in .NET MVC. I've tried several methods using Javascript but have had difficulties getting it to work in all browsers. So, how can I prevent this in my controller? It there some way that multiple submissions can be detected?
-
possible duplicate of [Troubleshooting anti-forgery token problems](http://stackoverflow.com/questions/5767768/troubleshooting-anti-forgery-token-problems) – Bohdan Lyzanets Jun 19 '14 at 12:07
-
Most of the answers below speak to using the form id. See this to set the form id: http://stackoverflow.com/q/2854616/3885927 – user3885927 May 31 '16 at 19:20
14 Answers
Updated answer for ASP.NET Core MVC (.NET Core & .NET 5.0)
Update note: Remember ASP.NET Core is still called "Core" in .NET 5.0.
I'm going to stick to the least-impact use case like before, where you're only adorning those controller actions that you specifically want to prevent duplicate requests on. If you want to have this filter run on every request, or want to use async, there are other options. See this article for more details.
The new form tag helper now automatically includes the AntiForgeryToken so you no longer need to manually add that to your view.
Create a new ActionFilterAttribute
like this example. You can do many additional things with this, for example including a time delay check to make sure that even if the user presents two different tokens, they aren't submitting multiple times per minute.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class PreventDuplicateRequestAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext context) {
if (context.HttpContext.Request.HasFormContentType && context.HttpContext.Request.Form.ContainsKey("__RequestVerificationToken")) {
var currentToken = context.HttpContext.Request.Form["__RequestVerificationToken"].ToString();
var lastToken = context.HttpContext.Session.GetString("LastProcessedToken");
if (lastToken == currentToken) {
context.ModelState.AddModelError(string.Empty, "Looks like you accidentally submitted the same form twice.");
}
else {
context.HttpContext.Session.SetString("LastProcessedToken", currentToken);
}
}
}
}
By request, I also wrote an asynchronous version which can be found here.
Here's a contrived usage example of the custom PreventDuplicateRequest
attribute.
[HttpPost]
[ValidateAntiForgeryToken]
[PreventDuplicateRequest]
public IActionResult Create(InputModel input) {
if (ModelState.IsValid) {
// ... do something with input
return RedirectToAction(nameof(SomeAction));
}
// ... repopulate bad input model data into a fresh viewmodel
return View(viewModel);
}
A note on testing: simply hitting back in a browser does not use the same AntiForgeryToken. On faster computers where you can't physically double click the button twice, you'll need to use a tool like Fiddler to replay your request with the same token multiple times.
A note on setup: Core MVC does not have sessions enabled by default. You'll need to add the Microsoft.AspNet.Session
package to your project, and configure your Startup.cs
properly. Please read this article for more details.
Short version of Session setup is:
In Startup.ConfigureServices()
you need to add:
services.AddDistributedMemoryCache();
services.AddSession();
In Startup.Configure()
you need to add (before app.UseMvc()
!!):
app.UseSession();
Original answer for ASP.NET MVC (.NET Framework 4.x)
First, make sure you're using the AntiForgeryToken on your form.
Then you can make a custom ActionFilter:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class PreventDuplicateRequestAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if (HttpContext.Current.Request["__RequestVerificationToken"] == null)
return;
var currentToken = HttpContext.Current.Request["__RequestVerificationToken"].ToString();
if (HttpContext.Current.Session["LastProcessedToken"] == null) {
HttpContext.Current.Session["LastProcessedToken"] = currentToken;
return;
}
lock (HttpContext.Current.Session["LastProcessedToken"]) {
var lastToken = HttpContext.Current.Session["LastProcessedToken"].ToString();
if (lastToken == currentToken) {
filterContext.Controller.ViewData.ModelState.AddModelError("", "Looks like you accidentally tried to double post.");
return;
}
HttpContext.Current.Session["LastProcessedToken"] = currentToken;
}
}
}
And on your controller action you just...
[HttpPost]
[ValidateAntiForgeryToken]
[PreventDuplicateRequest]
public ActionResult CreatePost(InputModel input) {
...
}
You'll notice this doesn't prevent the request altogether. Instead it returns an error in the modelstate, so when your action checks if ModelState.IsValid
then it will see that it is not, and will return with your normal error handling.

- 2,063
- 15
- 21
-
12This is the best answer to this question in my opinion. When, for whatever reason, client-side prevention of double-submission won't work, this provides a neat way of catching it on the server-side. Kudos for the use of an action attribute and repurposing the anti-forgery helper to provide a one-time token, it makes it really easy to apply this. – ngm May 09 '16 at 09:11
-
Great answer, thanks Jim, it's good to mention this will not work when using LB, unless if share session has been implemented. – InkHeart Jun 29 '16 at 02:13
-
Any reason not to replace HttpContext.Current.Session with filterContext.Controller.TempData ? – Roger Willcocks Jul 20 '16 at 02:55
-
1
-
-
Tried this but it didn't seem to work for me - did multiple quick submissions and despite getting the error most of the submits were processed by the server. – Rowan Oct 28 '16 at 00:37
-
It's not designed to prevent the server from processing the request, it's designed to return a model state error. Without seeing your code, all I can say is you may not be handling the model state errors properly in your controller. HttpContext.Current.Request is thread-safe and unique per request, if that was your concern. – Jim Yarbro Oct 28 '16 at 06:41
-
3@JimYarbro Even though your solution works great and prevent duplicate submission but there is still a problem I can see. Let's assume, I was updating the user profile, when the first request made all changes made in the database but just before it completes (redirect to another page, in my case), second request has been made and eventually user will see the error message about the duplicate submission but he will never know his profile has been updated when he made first request? Is it possible to notify user about the changes there were made during first request? – Ammar Khan Dec 02 '16 at 12:06
-
1@user2866746 Generally speaking, what you're describing is notifying a user of action results from another action. This is conventionally handled by passing messages in `TempData[]` before `RedirectToAction()` is called. – Jim Yarbro Dec 02 '16 at 13:02
-
It doesn't work for me in ASP.NET Core, when I debug I see that this happens in multiple threads. – Neme Aug 24 '17 at 21:00
-
@neme Core 1.1? I believe core 2.0 is out now and I haven't reviewed the changes yet. – Jim Yarbro Aug 25 '17 at 21:19
-
Yes I already upgraded to 2.0, sorry, I don't know if it would've worked in 1.1... I was able to solve it by wrapping the method in `context.HttpContext.Session.LoadAsync().Wait()` and `context.HttpContext.Session.CommitAsync().Wait()`, but it doesn't seem like a good solution when I have to use Wait... – Neme Aug 25 '17 at 21:22
-
@Neme please check my solution for an asynchronous version and report back if it solved your issue please! – Jim Yarbro Sep 14 '17 at 21:05
-
2It is not ideal to throw error if user has clicked the 'Save' button 2 times... to me, it makes more sense to prevent user from clicking the button 2 times – Hooman Bahreini May 01 '18 at 22:45
-
@Hooman Yes, however that wasn't the question. This answer is how to handle this situation without javascript. – Jim Yarbro May 03 '18 at 18:18
-
This should be the accepted answer and a standard part of the ASP.Net MVC framework, IMHO. Would be awesome if you could also show how you would present the output of the first request processed rather than showing model errors for duplicate requests. – CShark Jun 26 '18 at 19:59
-
5Just going to add to this awesome answer. An above comment asked how to deal with letting the user know that the first post was successful. I changed the Attribute to add the error to the ModelState with key "DuplicatePost". Then in my PostAction (protected by PreventDuplicateRequest) I put: if(ModelState.ContainsKey("DuplicatePost") return new EmptyResult(); This forces the server to not return any page for the subsequent posts and it only returns your normal processing from the first! – Red Nightingale Sep 04 '18 at 01:52
-
Does the use of 'lock' here not have the potential to cause a bit of a bottleneck? My understanding was that it would mean only one thread at a time would be execute that piece of code. If that was used on multiple Action Methods, wouldn't this mean that concurrent requests by different users would have to wait? Or am I misunderstanding how lock works? – chipples Oct 30 '18 at 09:35
-
@chipples locking only adds about 50ns of overhead to allocate. The slow part is in waiting for whatever is happening within the lock. If you reduce external calls and memory access within the lock, then you won't add much overhead at all. In any case, I ended up not using a lock in the updated version at the bottom as it was ultimately distracting from the simplified answer. – Jim Yarbro Oct 30 '18 at 13:51
-
This is a great answer but when testing I found that its pretty temperamental. Sometimes it works, other times it doesn't. – Bad Dub Feb 04 '20 at 16:19
-
@BadDub that may be due to nuances in your application or environment. For example you may be running this on a load balanced system, or your sessions may be impacted by other settings which causes the session to get dumped prematurely, etc. You can try storing the value in some other cache mechanism and see if that stabilizes it for you. – Jim Yarbro Feb 05 '20 at 07:54
-
@BadDub I just discovered a bug that was caused when this attribute was used on a request that is not a form. Adding a check for `context.HttpContext.Request.HasFormContentType` resolved it, and might resolve your problem with inconsistency. – Jim Yarbro Nov 12 '20 at 11:21
-
@RedNightingale Your idea got me excited, but it just leaves me with a blank page in the browser. Did you do anything else beyond what you already said to get working? – MarredCheese Apr 13 '21 at 14:55
-
1+1 but note that this solution can fail if a user is posting from multiple browser tabs, [as pointed out by Mark Butler](https://stackoverflow.com/a/54237728/5405967) below. I posted [an expanded version of this answer](https://stackoverflow.com/a/67079200/5405967) that addresses that concern by tracking the last x tokens instead of just 1 past token. – MarredCheese Apr 13 '21 at 17:10
-
@MarredCheese EmptyResult() is a blank page result. You'll need to look at [the different types of ActionResult](https://www.c-sharpcorner.com/article/different-types-of-actionresult-in-mvc/) for other views. – Jim Yarbro May 18 '21 at 06:37
-
JimYarbro, I'm not sure what you were getting at with that last comment. What I was trying to say before is that if I use @RedNightingale's idea, it still works fine for normal, single submissions, but for duplicate submissions, the end result is a blank page in the browser, which is not helpful. It seems what is actually needed is to detect any duplicate requests and abort them rather than letting them generate responses. Is there a way to do that? From my experimentation, it seems returning `EmptyResult()` means "respond with nothing" rather than "don't respond at all." – MarredCheese May 18 '21 at 13:36
-
@MarredCheese right, EmptyResult is what results in the blank page. You always need to respond with something, either with a 404 not found, or a 500 internal error, or in this case replace the EmptyResult with some other ActionResult such as a View that displays an error message. You could also just throw an exception and then let your exception handler work it out, but that doesn't make for a clean user experience. – Jim Yarbro May 19 '21 at 14:05
-
This is the best solution. The accepted answer does nothing to prevent users from spamming the submit button. – Valuator Jul 19 '21 at 14:55
-
Some times it is not working, as every request execute in a new thread some times depending on how fast it works the second request gets executed. this part "if (HttpContext.Current.Session["LastProcessedToken"] == null)" return true in both requests. and then in the action the "ModelState.IsValid" also returns true and the request is executed twice. I think it is because the second request does not wait for the first one to finish, so they both execute at the same time and the session variable is not set in time – Angel Q Dec 07 '21 at 14:31
-
That is an interesting edge case. You could attempt to use a lock as seen in the original portion of my answer, however that won't help on distributed systems so just be aware of that. You'll either need to implement a post-process cleanup in that case that goes back and deconflicts to find the single truth, or you will need to implement a unique locking mechanism. Also see the answer from @MarredCheese below that covers a lot more than I can here. – Jim Yarbro Dec 08 '21 at 15:17
-
I have my Razor page app hosted on IIS 8.5 / Server 2012r2 for years and never had double submissions problem. I didn't even know that the submission idempotency is not builtin in Razor. As soon as I upgraded to IIS 10.0/Server 2019 I started to receive the problem in various parts of my app (and got very surprised). Obviously, there's a difference how IIS or OS handles transient http errors that leads to the duplicate problem. – Niksr Jun 15 '23 at 12:04
I've tried several methods using Javascript but have had difficulties getting it to work in all browsers
Have you tried using jquery?
$('#myform').submit(function() {
$(this).find(':submit').attr('disabled', 'disabled');
});
This should take care of the browser differences.

- 1,023,142
- 271
- 3,287
- 2,928
-
Yeah I've tried that particular solution and it doesn't work in some versions of IE, which is why I'm trying to find a non Javascript solution – Phil Hale Nov 22 '10 at 22:18
-
Apologies, that solution does work! I failed to notice that your answer was slightly different to the one I had used previously and it does work in all browser versions I tested – Phil Hale Nov 22 '10 at 23:23
-
4you could mark as answer, so I would have read this before the other one ;) – VinnyG Mar 23 '11 at 20:10
-
2While it's preventing a second click, it's also eating the click event so my form submit no longer works :( – SteveCav Jul 24 '13 at 04:01
-
5I found it necessary to check if $("form").valid() otherwise it would be stuck on disable when there was validation errors – John Nov 26 '13 at 07:26
Just to complete the answer of @Darin, if you want to handle the client validation (if the form has required fields), you can check if there's input validation error before disabling the submit button :
$('#myform').submit(function () {
if ($(this).find('.input-validation-error').length == 0) {
$(this).find(':submit').attr('disabled', 'disabled');
}
});

- 6,883
- 7
- 58
- 76
-
12Thanks, FYI: this solution also worked with unobtrusive validation (including server side). I tried to rapid-click the upvote button to give you more points, but StackOverflow blocked multiple submits... :-) – Robert Corvus Feb 06 '12 at 17:33
-
1what if we use $(this).valid()
$('form').submit(function () { ('.input-validation-error').length); if ($(this).valid()) { $(this).find(':submit').attr('disabled', 'disabled'); } }); – aamir sajjad Jul 28 '14 at 16:56 -
Great improvement... another note in case people have multiple submit buttons with different values (save/delete), that **disabled controls do not submit their values**. To rectify, you could either a) save value as a hidden input or b) mimic disabled attribute with CSS to grey out and JS to prevent clicks – KyleMit Sep 19 '17 at 13:11
What if we use $(this).valid()
?
$('form').submit(function () {
if ($(this).valid()) {
$(this).find(':submit').attr('disabled', 'disabled');
}
});

- 17,541
- 8
- 92
- 91

- 3,019
- 1
- 27
- 26
-
1I found this is the right way. Using "($(this).find('.input-validation-error').length == 0)" from another answer doesn't work for me. That option disables the submit button even when there are validation errors. It could be because "($(this).find('.input-validation-error').length == 0)" is being called before the validation occurs. – user3885927 May 31 '16 at 19:16
-
Anyone use this and find any scenarios where this didn't work for them? – Ciaran Gallagher Jul 25 '19 at 09:13
-
The `:submit` pseudo element selector didn't work for me with a ` – carlin.scott Nov 26 '19 at 23:34
-
1@carlin.scott `:submit` is a jQuery pseudo selector - documented here: https://api.jquery.com/submit-selector/ – Gareth Saul Feb 27 '20 at 08:40
Strategy
The truth is that you need several lines of attack for this problem:
- The Post/Redirect/Get (PRG) pattern is not enough by itself. Still, it should always be used to provide the user with good experiences when using back, refresh, etc.
- Using JavaScript to prevent the user from clicking the submit button multiple times is a must because it provides a much less jarring user experience compared to server-side solutions.
- Blocking duplicate posts solely on the client side doesn't protect against bad actors and does not help with transient connection problems. (What if your first request made it to the server but the response did not make it back to the client, causing your browser to automatically resend the request?)
I'm not going to cover PRG, but here are my answers for the other two topics. They build upon the other answers here. FYI I'm using .NET Core 3.1.
Client-Side
Assuming you are using jQuery validation, I believe this is the cleanest/most efficient way to prevent your form submit button from being double-clicked. Note that submitHandler
is only called after validation has passed, so there is no need to re-validate.
$submitButton = $('#submitButton');
$('#mainForm').data('validator').settings.submitHandler = function (form) {
form.submit();
$submitButton.prop('disabled', true);
};
An alternative to disabling the submit button is to show an overlay in front of the form during submission to 1) block any further interaction with the form and 2) communicate that the page is "doing something." See this article for more detail.
Server-Side
I started off with Jim Yarbro's great answer above, but then I noticed Mark Butler's answer pointing out how Jim's method fails if someone submits forms via multiple browser tabs (because each tab has a different token and posts from different tabs can be interlaced). I confirmed that such a problem really does exist and then decided to upgrade from tracking just the last token to tracking the last x tokens.
To facilitate that, I made a couple of helper classes: one for storing the last x tokens and one for making it easy to store/retrieve objects to/from session storage. The main code now checks that the current token is not found in the token history. Other than that, the code is pretty much the same. I just made some little tweaks to suit my tastes. I included both the regular and asynchronous versions. The full code is below, but these are the critical lines:
var history = session.Get<RotatingHistory<string>>(HistoryKey) ?? new RotatingHistory<string>(HistoryCapacity);
if (history.Contains(token))
{
context.ModelState.AddModelError("", DuplicateSubmissionErrorMessage);
}
else
{
history.Add(token);
}
Sadly, the fatal flaw of this approach is that the feedback from the first post (before any duplicates) gets lost. A better (but much more complex) solution would be to store the result of each unique request by GUID, and then handle duplicate requests by not only skipping doing the work again but also returning the same result from the first request, giving the user a seamless experience. This thorough article detailing Air BnB's methods of avoiding duplicate payments will give you an idea of the concepts.
PreventDuplicateFormSubmissionAttribute.cs
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
// This class provides an attribute for controller actions that flags duplicate form submissions
// by adding a model error if the request's verification token has already been seen on a prior
// form submission.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class PreventDuplicateFormSubmissionAttribute: ActionFilterAttribute
{
const string TokenKey = "__RequestVerificationToken";
const string HistoryKey = "RequestVerificationTokenHistory";
const int HistoryCapacity = 5;
const string DuplicateSubmissionErrorMessage =
"Your request was received more than once (either due to a temporary problem with the network or a " +
"double button press). Any submissions after the first one have been rejected, but the status of the " +
"first one is unclear. It may or may not have succeeded. Please check elsewhere to verify that your " +
"request had the intended effect. You may need to resubmit it.";
public override void OnActionExecuting(ActionExecutingContext context)
{
HttpRequest request = context.HttpContext.Request;
if (request.HasFormContentType && request.Form.ContainsKey(TokenKey))
{
string token = request.Form[TokenKey].ToString();
ISession session = context.HttpContext.Session;
var history = session.Get<RotatingHistory<string>>(HistoryKey) ?? new RotatingHistory<string>(HistoryCapacity);
if (history.Contains(token))
{
context.ModelState.AddModelError("", DuplicateSubmissionErrorMessage);
}
else
{
history.Add(token);
session.Put(HistoryKey, history);
}
}
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
HttpRequest request = context.HttpContext.Request;
if (request.HasFormContentType && request.Form.ContainsKey(TokenKey))
{
string token = request.Form[TokenKey].ToString();
ISession session = context.HttpContext.Session;
await session.LoadAsync();
var history = session.Get<RotatingHistory<string>>(HistoryKey) ?? new RotatingHistory<string>(HistoryCapacity);
if (history.Contains(token))
{
context.ModelState.AddModelError("", DuplicateSubmissionErrorMessage);
}
else
{
history.Add(token);
session.Put(HistoryKey, history);
await session.CommitAsync();
}
await next();
}
}
}
RotatingHistory.cs
using System.Linq;
// This class stores the last x items in an array. Adding a new item overwrites the oldest item
// if there is no more empty space. For the purpose of being JSON-serializable, its data is
// stored via public properties and it has a parameterless constructor.
public class RotatingHistory<T>
{
public T[] Items { get; set; }
public int Index { get; set; }
public RotatingHistory() {}
public RotatingHistory(int capacity)
{
Items = new T[capacity];
}
public void Add(T item)
{
Items[Index] = item;
Index = ++Index % Items.Length;
}
public bool Contains(T item)
{
return Items.Contains(item);
}
}
SessonExtensions.cs
using System.Text.Json;
using Microsoft.AspNetCore.Http;
// This class is for storing (serializable) objects in session storage and retrieving them from it.
public static class SessonExtensions
{
public static void Put<T>(this ISession session, string key, T value) where T : class
{
session.SetString(key, JsonSerializer.Serialize(value));
}
public static T Get<T>(this ISession session, string key) where T : class
{
string s = session.GetString(key);
return s == null ? null : JsonSerializer.Deserialize<T>(s);
}
}

- 17,541
- 8
- 92
- 91
You could include a hidden (random or counter) value in the form post, a controller could track these values in an 'open' list or something similar; every time your controller hands out a form it embeds a value, which it tracks allowing one post use of it.
In its self, no, however depending on what the controller is actually doing, you should be able to work out a way.
Is a record being created in the database that you can check for to see if they've already submitted the form?

- 19,961
- 7
- 57
- 90
-
I think that would be possible, but I was hoping to find a different method – Phil Hale Nov 22 '10 at 22:27
Use the Post/Redirect/Get design pattern.
PS:
It looks to me that the answer by Jim Yarbro could have a fundamental flaw in that the __RequestVerificationToken
stored in the HttpContext.Current.Session["LastProcessedToken"]
will be replaced when a second form is submitted (from say another browser window). At this point, it is possible to re-submit the first form without it being recognized as a duplicate submission. For the proposed model to work, wouldn’t a history of __RequestVerificationToken
be required? This doesn't seem feasible.

- 17,541
- 8
- 92
- 91

- 21
- 2
-
1Mark, please post this as a new question instead of writing it in this answer section. In that new question, you may link to this original question. – Rosdi Kasim Jan 20 '19 at 09:11
-
I'm glad you pointed out that flaw in Jim's answer. I just posted [my own answer](https://stackoverflow.com/a/67079200/5405967) that solves the problem by checking the current token against the last x tokens instead of just the last 1 token. – MarredCheese Apr 13 '21 at 16:59
-
Couldn't the accepted answer just be modified to use the current request path together with "LastProcessedToken" as the Session key to track each form independently? – Valuator Jul 19 '21 at 15:09
Just add this code at the end of your page. I am using "jquery-3.3.1.min.js" and "bootstrap 4.3.1"
<script type="text/javascript">
$('form').submit(function () {
if ($(this).valid()) {
$(this).find(':submit').attr('disabled', 'disabled');
}
});
</script>

- 109
- 1
- 4
This works on every browser
document.onkeydown = function () {
switch (event.keyCode) {
case 116: //F5 button
event.returnValue = false;
event.keyCode = 0;
return false;
case 82: //R button
if (event.ctrlKey) {
event.returnValue = false;
event.keyCode = 0;
return false;
}
}
}

- 11
-
This uses javascript. The OP is looking for a no javascript solution. – Ben Bartle Oct 29 '14 at 06:31
-
1
You can also pass some sort of token in a hidden field and validate this in the controller.
Or you work with redirects after submitting values. But this get's difficult if you take heavily advantage of ajax.

- 1,335
- 1
- 14
- 24
Dont reinvent the wheel :)
Use the Post/Redirect/Get design pattern.
Here you can find a question and an answer giving some suggestions on how to implement it in ASP.NET MVC.
-
62the PRG pattern is indeed very good and I would also recommend it but I don't see how it could solve the issue. Let's take for example a slow POST action which processes a payment. The user clicks on the submit button and it might take some time before his request is processed and he is redirected to the success page. He might wonder what's going on and whether he clicked on the submit button. So he clicks it a second time which leads to a second HTTP request being sent to the server with the same data and without knowing it he orders the product twice. – Darin Dimitrov Nov 23 '10 at 07:04
-
9According to the linked Wikipedia article the PRG pattern can't prevent duplicate form submission "if a web user clicks a submission button multiple times before the server response loads" – Phil Hale Nov 23 '10 at 09:10
-
@Darin: you're right but that problem could be easily solved by disabling the button after the click which, in any case, is only going to protect you in that specific moment. Please read more in my next comment @Phil Hale – Lorenzo Nov 23 '10 at 10:35
-
2@Phil Hale: yes. and then it continues saying `(may be prevented by using JavaScript to disable the button after the first click).` You can't simply protect yourself from all the cases that can cause a double submission using only one method. The PRG pattern does give you an experienced way to protect from most of the pitfalls. Adding a javascript is only going to protect you in that specific moment – Lorenzo Nov 23 '10 at 10:36
-
5Not sure why it is marked as an answer if it needs to use javascript – Alberto Montellano Jul 14 '14 at 22:10
-
2Or in the case I'm currently experiencing where the corporate proxy is "helpful" and decides that if it hasn't had a response in 1 minute, it should resubmit the request. – Roger Willcocks Sep 05 '17 at 03:27
-
@AlbertoMontellano Cause the user can still disable the javascript, so this handles server side as well and is actually the way to do this. If you return to the same page after posting the user can press F5 to refresh page and repeat submission – Jackal Oct 15 '19 at 08:05
You can do this by creating some sort of static entry flag that is user specific, or specific to whatever way you want to protect the resource. I use a ConcurrentDictionary to track entrance. The key is basically the name of the resource I'm protecting combined with the User ID. The trick is figuring out how to block the request when you know it's currently processing.
public async Task<ActionResult> SlowAction()
{
if(!CanEnterResource(nameof(SlowAction)) return new HttpStatusCodeResult(204);
try
{
// Do slow process
return new SlowProcessActionResult();
}
finally
{
ExitedResource(nameof(SlowAction));
}
}
Returning a 204 is a response to the double-click request that will do nothing on the browser side. When the slow process is done, the browser will receive the correct response for the original request and act accordingly.

- 609
- 4
- 7
Use this simple jquery input field and will work awesomely even if you have multiple submit buttons in a single form.
$('input[type=submit]').click(function () {
var clickedBtn = $(this)
setTimeout(function () {
clickedBtn.attr('disabled', 'disabled');
}, 1);
});

- 732,580
- 175
- 1,330
- 1,459

- 1
- 2