Réponse actualisée pour ASP.NET Core MVC (.NET Core & .NET 5.0)
Note de mise à jour : N'oubliez pas qu'ASP.NET Core est encore appelé "Core" dans .NET 5.0 .
Comme précédemment, je vais m'en tenir au cas d'utilisation le moins impactant, où vous n'ornez que les actions du contrôleur pour lesquelles vous souhaitez spécifiquement éviter les requêtes en double. Si vous voulez que ce filtre s'exécute à chaque requête, ou si vous voulez utiliser l'asynchronisme, il existe d'autres options. Voir cet article pour plus de détails.
Le nouvel assistant de balise de formulaire inclut désormais automatiquement l'AntiForgeryToken, de sorte que vous ne devez plus l'ajouter manuellement à votre vue.
Créer un nouveau ActionFilterAttribute
comme cet exemple. Vous pouvez faire beaucoup d'autres choses avec cela, par exemple inclure une vérification du délai pour s'assurer que même si l'utilisateur présente deux jetons différents, il ne soumet pas plusieurs fois par 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);
}
}
}
}
A la demande, j'ai également écrit une version asynchrone. que l'on peut trouver ici .
Voici un exemple artificiel d'utilisation de la coutume PreventDuplicateRequest
attribut.
[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);
}
Une remarque sur les tests : le simple fait de cliquer sur le bouton "Retour" dans un navigateur n'utilise pas le même AntiForgeryToken. Sur les ordinateurs plus rapides où vous ne pouvez pas physiquement double-cliquer deux fois sur le bouton, vous devrez utiliser un outil tel que Fiddler pour rejouer votre demande avec le même jeton plusieurs fois.
Une note sur la configuration : Core MVC n'a pas de sessions activées par défaut. Vous devrez ajouter l'option Microsoft.AspNet.Session
à votre projet, et configurez votre Startup.cs
correctement. Veuillez lire cet article pour plus de détails.
La version courte de la configuration de la session est : Dans Startup.ConfigureServices()
que vous devez ajouter :
services.AddDistributedMemoryCache();
services.AddSession();
En Startup.Configure()
vous devez ajouter ( avant app.UseMvc()
! !):
app.UseSession();
Réponse originale pour ASP.NET MVC (.NET Framework 4.x)
Tout d'abord, assurez-vous que vous utilisez l'AntiForgeryToken sur votre formulaire.
Vous pouvez ensuite créer un ActionFilter personnalisé :
[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;
}
}
}
Et sur votre action de contrôle, vous avez juste...
[HttpPost]
[ValidateAntiForgeryToken]
[PreventDuplicateRequest]
public ActionResult CreatePost(InputModel input) {
...
}
Vous remarquerez que cela n'empêche pas complètement la demande. Au lieu de cela, il renvoie une erreur dans l'état du modèle, de sorte que lorsque votre action vérifie si les conditions suivantes sont remplies ModelState.IsValid
alors il verra que ce n'est pas le cas, et reviendra avec votre traitement normal des erreurs.
0 votes
Duplicata possible de Dépannage des problèmes de jeton anti-falsification
0 votes
La plupart des réponses ci-dessous concernent l'utilisation de l'identifiant de formulaire. Voir ceci pour définir l'identifiant du formulaire : stackoverflow.com/q/2854616/3885927