Skip to content

Instantly share code, notes, and snippets.

@jyarbro
Last active May 23, 2024 11:56
Show Gist options
  • Save jyarbro/facb175caed8eb9e2239faf750e37230 to your computer and use it in GitHub Desktop.
Save jyarbro/facb175caed8eb9e2239faf750e37230 to your computer and use it in GitHub Desktop.
MVC Core async attribute for preventing duplicate requests.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class PreventDuplicateRequestAttribute : ActionFilterAttribute {
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) {
if (context.HttpContext.Request.Form.ContainsKey("__RequestVerificationToken")) {
await context.HttpContext.Session.LoadAsync();
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);
await context.HttpContext.Session.CommitAsync();
}
}
await next();
}
}
@kadiryunusdemir
Copy link

Sometimes it doesn't work because each request runs in a new thread. If the second request runs quickly, "lastToken" might be null for both requests. Then "ModelState.IsValid" is true for both, causing the request to execute twice. I think this happens because the second request doesn't wait for the first one to finish, so the session variable isn't set in time. I used SemaphoreSlim class to overcome that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment