查看原文
其他

.NET Core 中如何有效屏蔽重复提交

DotNet 2022-07-19

The following article is from NET技术问答 Author StackOverflow

问题


我通过 Post 方式向我的一个 WebAPI 中提交数据,然后插入到数据库中,在 UI端,当用户点击某一个 Button 之后,代码会将 Button 禁用,但因为某些原因,点击按钮的速度比禁用按钮的函数还要快,这就造成了 Post 两次的情况,也就插入了两条同样的数据。


在客户端我用 axios 来做 Post 提交,请问我如何在 Server 端规避这种事情?


回答


前段时间刚好遇到了这个场景,我创建了一个 ActionFilter,然后使用了 Anti Fogery Token ,参考如下代码:首先启用 session。

services.Configure<CookiePolicyOptions>(options =>  
{  
    // This lambda determines whether user consent for non-essential cookies is needed for a given request.  
    options.CheckConsentNeeded = Context => false;  
    options.MinimumSameSitePolicy = SameSiteMode.None;  
});  

che();  
services.AddSession(options => {  
    // Set a short timeout for easy testing.  
    options.IdleTimeout = TimeSpan.FromMinutes(10);  
    options.Cookie.HttpOnly = true;  
    // Make the session cookie essential  
    options.Cookie.IsEssential = true;  
});  
  

然后就可以 use 了。

 app.UseSession();  
  

接下来定义一个防重复提交的 Attribute 。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]  
public class PreventDoublePostAttribute : ActionFilterAttribute  
{  
    private const string UniqFormuId = "LastProcessedToken";  
    public override async void OnActionExecuting(ActionExecutingContext context)  
    {  
  
        IAntiforgery antiforgery = (IAntiforgery)context.HttpContext.RequestServices.GetService(typeof(IAntiforgery));  
        AntiforgeryTokenSet tokens = antiforgery.GetAndStoreTokens(context.HttpContext);  
  
        if (!context.HttpContext.Request.Form.ContainsKey(tokens.FormFieldName))  
        {  
            return;  
        }  
  
        var currentFormId = context.HttpContext.Request.Form[tokens.FormFieldName].ToString();  
        var lastToken = "" + context.HttpContext.Session.GetString(UniqFormuId);  
  
        if (lastToken.Equals(currentFormId))  
        {  
            context.ModelState.AddModelError(string.Empty, "Looks like you accidentally submitted the same form twice.");  
            return;  
        }  
        context.HttpContext.Session.Remove(UniqFormuId);  
        context.HttpContext.Session.SetString(UniqFormuId, currentFormId);  
        await context.HttpContext.Session.CommitAsync();  
  
    }  
  
}  

然后在需要该验证规则的 Action 上进行标注。

[HttpPost]  
[PreventDoublePost]  
public async Task<IActionResult> Edit(EditViewModel model)  
{  
    if (!ModelState.IsValid)  
    {  
        //PreventDoublePost Attribute makes ModelState invalid  
    }  
    throw new NotImplementedException();  
}  

关于如何生成 Anti Fogery Token,可以看下msdn: https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-2.2#javascript

总结

这是一个非常常见的需求,除了这种做法,通常用带有过期时间的cache来做,也是可以的,比如 3s 内只能有一个请求。

- EOF -

推荐阅读  点击标题可跳转

.NET 5 中使用 Consul+Ocelot+Polly缓存、限流、熔断、降级

案例分享 .NET 5+Identity+JWT+VS Code.NET Core 基于Quartz的UI可视化操作组件


看完本文有收获?请转发分享给更多人

推荐关注「DotNet」,提升.Net技能 

点赞和在看就是最大的支持❤️

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存