目录
介绍
自从我了解到ASP.NET Core 7及更新版本具有内置的速率限制以来,我一直想尝试一下。我终于有时间检查一下,这是我到目前为止发现的。这会在ASP.NET Core中引入速率限制的同时使用内置选项和AspNetCoreRateLimit。
背景
在我们开始之前快速评论一下。我过去写过关于这个主题的博客,所以我建议你通读这篇文章:使用ASP.NET Core和AspNetCoreRateLimit的速率限制API请求。在这篇文章中,我将比较ASP.NET Core中的内置速率限制功能与AspNetCoreRateLimit。我将尝试在这里总结上一篇文章,但为了获得完整的体验,请考虑阅读这两篇文章。
ASP.NET Core中的速率限制
Web应用程序中的速率限制通常是关于...很好地限制了Web应用程序处理的请求数量。对于公共API,您很快需要某种方法来限制用户可以发出的请求数。关于如何实现这一点,有一系列的可能性,无论是基于客户端的IP地址、API密钥/令牌还是第三方。为了将本文中的示例与上一篇文章中的AspNetCoreRateLimit示例进行比较,我将基于API密钥实现速率限制。
若要测试速率限制,需要.NET 7或更高版本以及最新版本的Visual Studio 2022(当前需要预览版才能运行.NET 7应用)。
通过运行以下命令创建新应用程序:
dotnet new mvc
这将创建一个新的MVC应用。速率限制并非特定于ASP.NET Core MVC,也可以与最小的API和其他类型一起使用。在新的Web应用程序中,安装Microsoft.AspNetCore.RateLimiting NuGet包:
dotnet add package Microsoft.AspNetCore.RateLimiting
在Program.c s文件中或(Startup.cs如果不喜欢顶级语句)包含如下速率限制配置:
builder.Services.AddRateLimiter(options =>
{
});
这将告诉ASP.NET Core您希望使用默认设置配置速率限制中间件。我想预先更改的一个默认参数是达到限制后应返回的状态代码。默认情况下Microsoft.AspNetCore.RateLimiting将返回状态代码503。要将其更改为(更正确的IMO)状态代码429,请将其作为选项的一部分提供:
builder.Services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
});
接下来,我们需要在达到限制时告知速率限制中间件。这是通过称为策略的概念完成的。速率限制中间件中提供了一系列不同的策略类型。在此示例中,我想限制使用URL中可用的相同API密钥发出的请求数,如下所示:
https://localhost:7126/?api_key=mykey
要对此进行配置,请包括以下策略:
builder.Services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
options.AddPolicy("apikey", httpContext =>
{
if (httpContext.Request.Query.Keys.Contains("api_key"))
{
return RateLimitPartition.GetFixedWindowLimiter(
httpContext.Request.Query["api_key"].ToString(),
fac =>
{
return new FixedWindowRateLimiterOptions
{
Window = TimeSpan.FromHours(1),
PermitLimit = 10,
};
});
}
else
{
return RateLimitPartition.GetNoLimiter("");
}
});
});
让我们逐行浏览策略。通过调用AddPolicy方法添加策略。第一个参数是我们需要引用此策略的策略名称。此名称将在以下步骤中使用。接下来,我们提供一个包含要为此策略运行的实际代码的Func。在回调中,我检查当前请求是否包含名为api_key的查询参数。在这种情况下,我们希望应用速率限制。否则,不应运行速率限制,这是通过返回GetNoLimiter的结果来完成的。
此速率限制策略基于固定窗口限制。如前所述,有一系列不同的策略类型可用,因此请务必在选择之前查看文档。固定窗口限制器将在指定的时间范围内对限制请求进行速率处理。在此示例中,我允许使用相同的API密钥每小时发出10个请求。在现实世界中,您的API可能应该能够处理10个以上的请求,但为此,我选择了一个较小的数字来在浏览器中轻松测试它。
缺少的最后一步是告诉ASP.NET Core使用中间件以及在哪里应用策略。有一些属性可用于将其应用于特定操作和/或控制器,但对于此示例,我将将其应用于所有内容:
app.UseRateLimiter();
app
.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.RequireRateLimiting("apikey");
通过追加RequestRateLimiting到控制器设置并包括我们在上一步中设置的策略名称,ASP.NET Core将自动针对所有控制器运行策略。请记住,此处仅适用于具有查询参数api_key的终结点。
AspNetCoreRateLimit
这是一个非常基本的示例,说明如何使用Microsoft.AspNetCore.RateLimiting包实现速率限制。让我们快速重新访问该AspNetCoreRateLimit包,看看如何使用该包实现类似的策略。
AspNetCoreRateLimit是另一个已经存在多年的限速库。它由Stefan Prodan维护,并为在ASP.NET Core中实现速率限制提供了一个非常灵活的模型。
首先安装AspNetCoreRateLimit NuGet包。如果您正在编码,则可以将其安装在与以前相同的项目中,但不建议为实际代码安装两个速率限制包:
dotnet add package AspNetCoreRateLimit
AspNetCoreRateLimit中的策略作为配置和客户端解析程序实现:
public class ElmahIoRateLimitConfiguration : RateLimitConfiguration
{
public ElmahIoRateLimitConfiguration(
IOptions<IpRateLimitOptions> ipOptions,
IOptions<ClientRateLimitOptions> clientOptions)
: base(ipOptions, clientOptions)
{
}
public override void RegisterResolvers()
{
base.RegisterResolvers();
ClientResolvers.Add(new ClientQueryStringResolveContributor());
}
}
public class ClientQueryStringResolveContributor : IClientResolveContributor
{
public Task<string> ResolveClientAsync(HttpContext httpContext)
{
var queryDictionary =
Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(
httpContext.Request.QueryString.ToString());
if (queryDictionary.ContainsKey("api_key")
&& !string.IsNullOrWhiteSpace(queryDictionary["api_key"]))
{
return Task.FromResult(queryDictionary["api_key"].ToString());
}
return Task.FromResult(Guid.NewGuid().ToString());
}
}
本文中的代码已更新以匹配最新版本的AspNetCoreRateLimit。有关AspNetCoreRateLimit工作原理的更多详细信息,请查看上一篇文章。上面的代码对应于我们使用另一个包指定的策略,该策略基于名为api_key的查询参数。
这些类可以在Program.cs文件中配置:
builder.Services.AddSingleton<IClientPolicyStore, MemoryCacheClientPolicyStore>();
builder.Services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
builder.Services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();
builder.Services.AddSingleton<IRateLimitConfiguration, ElmahIoRateLimitConfiguration>();
附加的单例告诉AspNetCoreRateLimit如何存储当前状态。上一个示例中的限制和时间跨度通过选项提供:
builder.Services.Configure<ClientRateLimitOptions>(options =>
{
options.GeneralRules = new List<RateLimitRule>
{
new RateLimitRule
{
Endpoint = "*",
Period = "1h",
Limit = 10,
}
};
});
同样,我们将请求限制为每小时10个。该Endpoint属性的*值与上一示例中将速率限制中间件包含在所有控制器中的方式相匹配。
唯一缺少的部分是调用该UseClientRateLimiting方法:
app.UseClientRateLimiting();
就是这样!现在,使用AspNetCoreRateLimit实现了类似的速率限制。无论您喜欢一个套餐还是另一个套餐,我都会留给您。这些示例仅显示了每个包的有限功能集,因此我建议您尝试两者并选择您喜欢的功能。
https://www.codeproject.com/Tips/5348197/Built-in-Rate-Limiting-in-ASP-NET-Core-vs