ASP.NET Core 请求限速的ActionFilter
文章目录
- 前言
- 一、实现步骤
- 1)创建自定义Action Filter
- 示例1:
- 示例2:
- 2)注册服务
- 3)使用
- 二、实现说明
- 总结
前言
以下是一个基于内存缓存实现的自定义限流Action Filter。
一、实现步骤
1)创建自定义Action Filter
示例1:
- MyRateLimitAttribute.cs
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Caching.Memory;public class MyRateLimitAttribute : TypeFilterAttribute{public MyRateLimitAttribute() :base(typeof(MyRateLimitFilter)){}public class MyRateLimitFilter : IAsyncActionFilter{private readonly IMemoryCache _memoryCache;public MyRateLimitFilter(IMemoryCache memoryCache){_memoryCache = memoryCache;}public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){string ip = context.HttpContext.Connection.RemoteIpAddress.ToString();if (string.IsNullOrEmpty(ip)){context.Result = new BadRequestObjectResult("Invalid Client IP");return;}string cacheKey = $"MyRateLimit_{ip}";_memoryCache.TryGetValue<long?>(cacheKey, out long? lastVisit);if (lastVisit == null || Environment.TickCount64 - lastVisit > 1000){_memoryCache.Set(cacheKey, Environment.TickCount64, TimeSpan.FromSeconds(10));await next();}else{context.Result = new ObjectResult("访问太频繁") { StatusCode=429}; }}}}
示例2:
- RateLimitAttribute.cs
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Caching.Memory;public class RateLimitAttribute : TypeFilterAttribute {public RateLimitAttribute(int maxRequests,int secondsWindow) : base(typeof(RateLimitFilter)){Arguments = new object[] { maxRequests,secondsWindow};}public class RateLimitFilter : IAsyncActionFilter{private readonly IMemoryCache _memoryCache;private readonly int _maxRequests;private readonly int _secondsWindow;public RateLimitFilter(IMemoryCache memoryCache, int maxRequests, int secondsWindow){_memoryCache = memoryCache;_maxRequests = maxRequests;_secondsWindow = secondsWindow;}public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){var ip=context.HttpContext.Connection.RemoteIpAddress?.ToString();if (string.IsNullOrEmpty(ip)){context.Result = new BadRequestObjectResult("Invalid client IP");return;}var cacheKey = $"RateLimit_{ip}";var windowStart = DateTime.UtcNow.AddSeconds(-DateTime.UtcNow.Second % _secondsWindow);if (!_memoryCache.TryGetValue(cacheKey, out RateLimitCounter counter) ||windowStart > counter.WindowStart){counter = new RateLimitCounter{Count = 1,WindowStart = windowStart,};}else{counter.Count++;}if (counter.Count > _maxRequests){context.Result = new ObjectResult("Too many requests"){StatusCode = 429};return;}_memoryCache.Set(cacheKey, counter, counter.WindowStart.AddSeconds(_secondsWindow));await next();}private class RateLimitCounter{public int Count { get; set; }public DateTime WindowStart { get; set; }}} }
2)注册服务
- 内存缓存服务
builder.Services.AddMemoryCache();
3)使用
- 示例:
[HttpGet] //[RateLimit(maxRequests: 5, secondsWindow: 60)] // 每分钟最多5次请求 [MyRateLimit] public async Task<ActionResult<Book>> GetAllBookAsync() {var res=await _bookRepository.GetAllAsync();return Ok(res); }
二、实现说明
- 使用IP地址识别客户端(需考虑代理场景)
- 基于固定时间窗口算法(每分钟/小时重置计数器)(示例2)
- 使用IMemoryCache存储计数器
- 返回429状态码(Too Many Requests)时阻止请求
总结
- 内存缓存方案仅适用于单实例部署
- 高并发场景建议使用Interlocked类处理计数器原子操作
- 生产环境推荐使用分布式缓存(如Redis)
- 建议使用成熟的限流库(如AspNetCoreRateLimit)