使用操作筛选器开发请求限流器

我们在操作筛选器中不仅可以在操作方法之前或者之后添加代码,还可以在满足条件的时候终止操作方法的执行。

我们知道,在操作筛选器中,我们通过await next()来执行下一个筛选器,如果没有下一个筛选器,程序就会执行目标操作方法。

如果我们不调用await next(),就可以终止操作方法的执行了。

我们将会通过开发一个实现请求限流器功能的操作筛选器演示如何终止操作方法的执行。

为了避免恶意客户端频繁发送大量请求而消耗服务器资源,我们要实现”1s内只允许最多有一个来自同一个IP地址的请求”。

可以通过自定义操作筛选器来实现,如以下代码所示:

C#
  public class RateLimitFilter(IMemoryCache _cache): IAsyncActionFilter
  {
      public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
      {
          

          string? removeIp = context.HttpContext.Connection.RemoteIpAddress?.ToString();

          string cacheKey = $"LastVisitTick_{removeIp}";

          long? lastTick = _cache.Get<long?>(cacheKey);

          if(lastTick == null || Environment.TickCount64-lastTick>1000)
          {

              _cache.Set(cacheKey,Environment.TickCount64,TimeSpan.FromSeconds(10));

              return next();

          }
          else
          {
              context.Result = new ContentResult { StatusCode = 429 };

              return Task.CompletedTask;
          }



      }
  }

这里通过注入的IMemoryCache来记录用户上一次访问的时间戳,在分布式系统下我们可以改用分布式缓存来代替内存缓存。

在第7、9行代码中,我们获取客户端的IP地址,并且用它来拼接缓存键;

在第11~20行代码中,我们获取这个客户端IP地址上一次访问服务器的时间,如果缓存中不存在上一次访问时间或者上一次访问距离现在已经超过1s,则在第18行代码中通过next来执行后面的筛选器;

如果上一次访问时间距离现在步超过1s,则向客户端发送HTTP状态码为429的响应,也就是”访问过于频繁”;

由于在第23、25行代码中,我们没有调用next,因此目标操作方法就得不到执行了。

接下来,不要忘了在Program.cs中注册RateLimitFilter并添加对内存缓存的支持,如以下代码:

C#
builder.Services.Configure<MvcOptions>(option => 
{

    option.Filters.Add<RateLimitFilter>();

});

接下来,启动项目,并且访问接口,如果访问频率不高的话,接口能够正常工作。

如果访问频率很高的话,服务器就会提示”Only once per second”。

订阅评论
提醒
0 评论
最旧
最新 最多投票
内联反馈
查看所有评论
滚动至顶部