ASP.NET Core Filter通過緩存實現接口防重

接口防重複調用

緩存使用文檔請看這篇博客
ASP.NET Core Filter文檔請看這篇博客

添加Nuget包添加緩存

--Memory
Install-Package  Microsoft.Extensions.Caching.Memory

--Redis
Install-Package  Microsoft.Extensions.Caching.StackExchangeRedis

Program.cs添加配置


Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

var builder = WebApplication.CreateBuilder(args);

//Memory
builder.Services.AddDistributedMemoryCache();

//redis
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "192.168.1.5:6379,password=123456,ssl=False,abortConnect=False";
    options.InstanceName = "App-Instance-Keys";
});

去重RequestRestrictionsAttribute代碼

注意這裏沒有區分Url QueryString和Request Header邏輯

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Caching.Distributed;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

namespace WebApplication1
{
    /// <summary>
    /// API限重Filter
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
    public sealed class RequestRestrictionsAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// 單次請求最小鎖定時間(毫秒)
        /// </summary>
        private readonly int _requestLocktime;

        /// <summary>
        /// 請求控制是否包含Body參數
        /// </summary>
        private readonly bool _includeArguments;

        /// <summary>
        /// Message
        /// </summary>
        private readonly string _message;

        /// <summary>
        /// 緩存Key
        /// </summary>
        private static readonly string KEY_PRIFIX_RequestLock = "myapp:apilock:path:{0}:arguments:{1}";

        /// <summary>
        /// 請求限制
        /// </summary>
        /// <param name="requestLocktime">鎖定時間(毫秒),默認五秒</param>
        /// <param name="includeArguments">請求控制是否包含Body參數</param>
        /// <param name="message">錯誤提示</param>
        public RequestRestrictionsAttribute(int requestLocktime = 5000, bool includeArguments = true, string message = "頻繁請求, 請稍後重試")
        {
            _requestLocktime = requestLocktime;
            _includeArguments = includeArguments;
            _message = message;
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (context == null)
            {
                return;
            }
            var storage = context.HttpContext.RequestServices.GetService<IDistributedCache>();
            if (storage == null)
            {
                base.OnActionExecuting(context);
                return;
            }
            string requestPath = context.HttpContext.Request.Path;
            string actionArguments = _includeArguments ? JsonSerializer.Serialize(context.ActionArguments) : "arguments";

            //*****此處key最好加上context.HttpContext.Request.QueryString和請求頭用戶信息進行判斷*****
            string RequestLockCacheKey = string.Format(KEY_PRIFIX_RequestLock, requestPath, HashEncrypt(actionArguments));
            if (storage.GetString(RequestLockCacheKey) != null) throw new Exception(_message);
            storage.SetString(RequestLockCacheKey, "1", new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMilliseconds(_requestLocktime) });

            //轉入用戶Action操作
            base.OnActionExecuting(context);
        }

        private static string HashEncrypt(string str)
        {
            byte[] hashedDataBytes = MD5.HashData(Encoding.GetEncoding("gb2312").GetBytes(str));
            //.net 5+
            return Convert.ToHexString(hashedDataBytes);

            //.net 5以下版本
            //StringBuilder sb = new();
            //foreach (byte i in hashedDataBytes) sb.Append(i.ToString("x2"));
            //return sb.ToString();


            //其他加密方式
            //byte[] hashedDataBytes = Encoding.GetEncoding("gb2312").GetBytes(str);
            //byte[] md5hashed32 = MD5.HashData(hashedDataBytes);
            //byte[] hashed40 = SHA1.HashData(hashedDataBytes);
            //byte[] hashed64 = SHA256.HashData(hashedDataBytes);
            //byte[] hashed128 = SHA512.HashData(hashedDataBytes);
        }
    }
}

Controller中使用

using Microsoft.AspNetCore.Mvc;

namespace WebApplication1.Controllers;

[ApiController]
[Route("[controller]")]
[RequestRestrictions(1000 * 5)]
public class WeatherForecastController : ControllerBase { } 

在方法中使用

[HttpGet]
[RequestRestrictions(1000 * 5)]
public IEnumerable<int> Get()
{
return Enumerable.Range(1, 5);
}

ApiRateLimit限流文檔

  1. AspNetCoreRateLimit
  2. Microsoft.AspNetCore.RateLimiting
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章