开发随笔记录——ASP.NET Core自定义中间件通过本地缓存实现客户端IP访问限流

声明:本文章中框架使用ASP.NET Core 3.1

首先,在appsettings.json中配置该中间件中需要使用的配置信息,如下:

  "RateLimiting": {
    "Count": 5, //限定时间内能够访问的次数
    "Seconds": 60 //达到访问上限后多少秒后可以重新访问
  }

其次,创建RequestRateLimitingMiddleware实现类,代码如下:

public class RequestRateLimitingMiddleware
    {
        //限定时间内能够访问的次数
        private readonly int Limit;
        //达到访问上限后多少秒后可以重新访问
        private readonly int Seconds;

        private readonly RequestDelegate next;

        private readonly IMemoryCache requestStore;

        public RequestRateLimitingMiddleware(RequestDelegate next, IConfiguration Configuration, IMemoryCache requestStore)
        {
            this.next = next;
            this.requestStore = requestStore;
            Limit = Configuration.GetValue<int>("RateLimiting:Count");
            Seconds = Configuration.GetValue<int>("RateLimiting:Seconds");
        }
       
        public async Task Invoke(HttpContext context)
        {
            var requestKey = $"{context.Connection.RemoteIpAddress.MapToIPv4()}-{context.Request.Method}-{context.Request.Path}";
            int hitCount = 0;
            var cacheOptions = new MemoryCacheEntryOptions() {
                AbsoluteExpiration = DateTime.Now.AddSeconds(Seconds)
            };

            if (requestStore.TryGetValue(requestKey, out hitCount))
            {
                if (hitCount < Limit)
                {
                    await ProcessRequest(context, requestKey, hitCount, cacheOptions);
                }
                else
                {
                    context.Response.Headers["X-RateLimit-RetryAfter"] = cacheOptions.AbsoluteExpiration?.ToString("yyyy-MM-dd HH:mm:ss");
                    context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
                }
            }
            else
            {
                await ProcessRequest(context, requestKey, hitCount, cacheOptions);
            }
        }
        private async Task ProcessRequest(HttpContext context,string requestKey,int hitCount,MemoryCacheEntryOptions cacheOptions)
        {
            hitCount++;
            requestStore.Set(requestKey, hitCount, cacheOptions);
            context.Response.Headers["X-RateLimit-Limit"] = Limit.ToString();
            context.Response.Headers["X-RateLimit-Remaining"] = (Limit - hitCount).ToString();
            await next(context);
        }
    }

稍后,在Startup.cs文件中,对自定义的中间件进行注册使用,如下:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseAuthorization();

            app.UseMiddleware<RequestRateLimitingMiddleware>();
            
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

最后,就可以进行测试了,测试结果对比如下:

提示:X-RateLimit-Limit代表在限定时间内可访问次数上限。

X-RateLimit-Remaining代表在限定时间内已经访问了多少次。
    X-RateLimit-RetryAfter代表什么时间后可再次重新进行访问。
posted @ 2021-08-16 13:46  苏瑾~  阅读(174)  评论(0)    收藏  举报