客户的APP反馈重复提交数据,需要阻止接口重复调用。
分享一段代码给大家:
/*===================================================================* 程序说明: WebApi开发框架(.NET8+EFCore+AspNetCore)* https://www.cscode.net/archive/webapi-netcore-v3/361414129516549.html* 原创作者: 珠海喜鹊信息技术有限公司* 创建日期: 2024/12/01 11:43:18* 最后修改: 2024/12/01 11:43:18 * 版权所有: Copyright 2006~2024, C/S框架网(www.csframework.com)*===================================================================*/
using CSFramework.WebApi.Common;
using CSFramework.WebApi.Core.Interfaces;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Text.RegularExpressions;
using System.Threading.Tasks;namespace CSFramework.WebApi.Core
{/// <summary>/// 不校验接口请求并发/// </summary>public class NotRequestConcurrentAttribute : BaseActionFilterAsyncAttribute { }/// <summary>/// 校验接口是否并发操作,重复请求(使用MemoryCache设置标记)/// </summary>public class RequestConcurrentAttribute : BaseActionFilterAsyncAttribute{public RequestConcurrentAttribute() { }/// <summary>/// 执行操作/// </summary>/// <param name="filterContext"></param>/// <returns></returns>public async override Task OnActionExecuting(ActionExecutingContext filterContext){//判断是否需要接口请求并发if (filterContext.ContainsFilter<NotRequestConcurrentAttribute>()){await base.OnActionExecuting(filterContext);return;}//当前登录用户IUserContext user = Core.Globals.ServiceProvider.GetService<IUserContext>();//获取当前登录用户账号if (user.UserId.IsEmpty()){await base.OnActionExecuting(filterContext);return;}Console.WriteLine(">>RequestConcurrentAttribute.OnActionExecuting...");//获取缓存key值,如:"userId_9b80d5d0ebda4b11c4ba396cd4312f35"var key = GetCacheKey(user.UserId, filterContext.HttpContext.Request.Path);var cache = Core.Globals.ServiceProvider.GetService<IMemoryCache>();string value = cache.Get<String>(key);//从缓存获取key//有标记,并发操作!if (!value.IsEmpty()){var errMessage = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " "+ "重复操作,系统已阻止。线程ID:"+ System.Threading.Thread.CurrentThread.ManagedThreadId;//保存数据日志var log = Core.Globals.ServiceProvider.GetService<IApiLogBusiness>();log.Log("concurrent", user.IP, "请求并发:" + errMessage);//删除缓存标记cache.Remove(key);throw new BizException(errMessage);}else{//设置缓存标记:同一个url,1秒内重复点击cache.Set<String>(key, "click", TimeSpan.FromSeconds(1));//设置1秒后自动过期。}await Task.CompletedTask;}public override Task OnActionExecuted(ActionExecutedContext context){return base.OnActionExecuted(context);}/// <summary>/// 生成缓存key/// </summary>/// <param name="userId"></param>/// <param name="requestPath"></param>/// <returns></returns>private string GetCacheKey(string userId, string requestPath){string localPath = GetApiPath(requestPath);var key = userId + "_" + localPath.ToMD5String();return key;}/// <summary>/// 获取api接口名称,如:[api/common/get] => [common/get]/// </summary>/// <param name="path"></param>/// <returns></returns>private string GetApiPath(string path){// 定义一个Regex对象实例Regex r = new Regex(@"(/api)?(/\S+)", RegexOptions.IgnoreCase);//在字符串中匹配Match m = r.Match(path);if (m.Success){string local = m.Groups[2].Value;return local.ToLower();}return "";}}
}
控制器定义特性:
[ApiController]
[RequestConcurrentAttribute]
public class _BaseController : ControllerBase
{//省略代码......
}
若忽略并发操作:
[NotRequestConcurrent]
[HttpGet]
public string Test()
{return string.Empty;
}