2026-5-18测试过的,完全可以直接用,测试项目是.net6.0
描述:分布式限流中间件,使用redis缓存访问次数
中间件
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using AIFast.Guard.OpenApiStructure.Utilities;
using AIFast.OpenApi.Structure.Access;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using StackExchange.Redis;
using Volo.Abp.DependencyInjection;namespace AIFast.OpenApi.Structure.Middlewares;public class OpenApiLimitMiddleware : IMiddleware, IScopedDependency
{private readonly IAccessInspector _accessInspector;private readonly ILogger<OpenApiLimitMiddleware> _logger;private readonly IDatabase _database;private readonly List<LimitConfig> _limitConfigs;public OpenApiLimitMiddleware(IAccessInspector accessInspector, ILogger<OpenApiLimitMiddleware> logger,IDatabase database){_accessInspector = accessInspector;_logger = logger;_database = database;_limitConfigs = new List<LimitConfig>{new LimitConfig { Url = "/api/plugin/manage/open/user-account-info/*", Method = "*", TimeWindowSeconds = 30, LimitCount = 3 } //配置示例1,可根据实际需求调整或删除//new LimitConfig { Url = "/api/plugin/manage/open/user-account-info/*", Method = "post", TimeWindowSeconds = 20, LimitCount = 2 } //配置示例2,可根据实际需求调整或删除//new LimitConfig { Url = "/api/plugin/manage/open/user-account-info/List", Method = "post", TimeWindowSeconds = 20, LimitCount = 2 } ////配置示例3,可根据实际需求调整或删除//new LimitConfig { Url = "/api/plugin/manage/open/user-account-info/get", Method = "POST", TimeWindowSeconds = 20, LimitCount = 2 },//new LimitConfig { Url = "/api/open/*", Method = "POST", TimeWindowSeconds = 1, LimitCount = 50 },//new LimitConfig { Url = "/api/open/*", Method = "PUT", TimeWindowSeconds = 1, LimitCount = 30 },//new LimitConfig { Url = "/api/open/*", Method = "DELETE", TimeWindowSeconds = 1, LimitCount = 20 },//new LimitConfig { Url = "/api/open/payment/*", Method = "POST", TimeWindowSeconds = 60, LimitCount = 100 }};}public async Task InvokeAsync(HttpContext context, RequestDelegate next){//_logger.LogInformation(context.Request.Path.ToString());var url = context.Request.Path.ToString().ToLower();var method = context.Request.Method.ToUpper();try{if (!await CheckLimitAsync(url, method)){await HandleLimitExceededAsync(context);return;}await next.Invoke(context);}catch (Exception ex){await OpenApiExceptionHandler.HandlerAsync(context, ex);}}private async Task<bool> CheckLimitAsync(string url, string method){var config = FindMatchingConfig(url, method);if (config == null){return true;}var cacheKey = GetLimitCacheKey(config.Url.ToLower(), config.Method, config.TimeWindowSeconds);var currentCount = await _database.StringIncrementAsync(cacheKey);if (currentCount == 1){await _database.KeyExpireAsync(cacheKey, TimeSpan.FromSeconds(config.TimeWindowSeconds));}return currentCount <= config.LimitCount;}private LimitConfig FindMatchingConfig(string url, string method){//查询指定Method的配置var result = _limitConfigs.FirstOrDefault(config =>WildcardMatch(url, config.Url.ToLower()) &&string.Equals(config.Method, method, StringComparison.OrdinalIgnoreCase));//如果指定Method的配置不存在,则查询*的配置if (result == null){result = _limitConfigs.FirstOrDefault(config =>WildcardMatch(url, config.Url.ToLower()) && config.Method == "*");}return result;}private bool WildcardMatch(string url, string pattern){if (pattern.EndsWith("/*")){var prefix = pattern.Substring(0, pattern.Length - 2);return url.StartsWith(prefix);}return url == pattern;}private async Task HandleLimitExceededAsync(HttpContext context){context.Response.StatusCode = 429;context.Response.ContentType = "application/json";await context.Response.WriteAsync("{\"code\":429,\"message\":\"请求过于频繁,请稍后再试\"}");}/// <summary>/// 获取限流缓存Key/// </summary>/// <param name="url">访问地址</param>/// <param name="method">请求方式</param>/// <param name="timeWindowSeconds">时间窗口(秒)</param>/// <returns>缓存key</returns>private string GetLimitCacheKey(string url, string method, int timeWindowSeconds){return $"limit:{url}_{method}_{timeWindowSeconds}s";}
}/// <summary>
/// 配置类
/// 可以调整数据存在json配置文件中,便于管理和扩展
/// </summary>
public class LimitConfig
{/// <summary>/// 需要限流的URL路径,支持通配符 */// 例如: "/api/open/*" 表示匹配所有以 /api/open/ 开头的路径/// </summary>public string Url { get; set; }/// <summary>/// HTTP请求方式/// 支持: GET, POST, PUT, DELETE 等; * 表示所有请求方式/// </summary>public string Method { get; set; }/// <summary>/// 限流时间窗口(单位:秒)/// 例如: 1 表示每秒限流,60 表示每分钟限流/// </summary>public int TimeWindowSeconds { get; set; }/// <summary>/// 在时间窗口内允许的最大请求次数/// </summary>public int LimitCount { get; set; }
}
二、使用
app.UseMiddleware<OpenApiLimitMiddleware>();年轻人,现在没钱算什么,以后没钱的日子还多着呢(-_-
