.NETCore WebApi阻止接口重复调用(并发操作)

客户的APP反馈重复提交数据,需要阻止接口重复调用。

分享一段代码给大家:

cs 复制代码
/*===================================================================
 *   程序说明: 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 "";
        }
    }
}

控制器定义特性:

cs 复制代码
[ApiController]
[RequestConcurrentAttribute]
public class _BaseController : ControllerBase
{
    //省略代码......
}

若忽略并发操作:

cs 复制代码
[NotRequestConcurrent]
[HttpGet]
public string Test()
{
    return string.Empty;
}
相关推荐
五VV4 小时前
Note25021902_TIA Portal V18 WinCC BCA Ed 需要.NET 3.5 SP1
.net
code_shenbing18 小时前
.NET MVC实现电影票管理
mvc·.net
shepherd枸杞泡茶1 天前
第3章 3.3日志 .NET Core日志 NLog使用教程
c#·asp.net·.net·.netcore
csdn_aspnet1 天前
ASP.NET Core 简单文件上传
asp.net·.netcore
时光追逐者1 天前
推荐几款开源免费的 .NET MAUI 组件库
microsoft·开源·c#·.net·.net core·maui
三天不学习1 天前
.Net面试宝典【刷题系列】
面试·职场和发展·.net
JosieBook1 天前
【.NET全栈】.NET包含的所有技术
.net
shepherd枸杞泡茶1 天前
第3章 3.2 配置系统 .NET Core配置系统
后端·c#·asp.net·.net
追逐时光者2 天前
Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
后端·.net
CodeCraft Studio2 天前
文档处理控件TX Text Control系列教程:使用 .NET C# 从 PDF 文档中提取基于模板的文本
pdf·c#·.net