ASP.NET Core Development Notes: Using Native Rate Limiting for Interfaces
I gave it a try, but it still feels like too much hassle. I'll just switch back to the original AspNetCoreRateLimit component.
目录12 个章节
Preface
Previously, I introduced using the AspNetCoreRateLimit component for API rate limiting (接口限流).
Starting from .Net7, AspNetCore began to include a built-in rate-limiting component. At that time, our project was still on .Net6, so we could only use a third-party one.
Now that we have upgraded to .Net8, it's time to try out this native component.
After trying it out: Configuration and usage are quite simple, but its functionality isn't as flexible as AspNetCoreRateLimit.
Registering Services
To keep the code in Program.cs concise, we continue to use extension methods for service registration and configuration.
In the file src/IdsLite.Api/Extensions/CfgRateLimit.cs:
public static class RateLimitPolicies {
public const string Fixed = "fixed";
public const string Sliding = "sliding";
}
public static class CfgRateLimit {
public static IServiceCollection AddRateLimit(this IServiceCollection services) {
services.AddRateLimiter(options => {
options.AddFixedWindowLimiter(RateLimitPolicies.Fixed, opt => {
opt.Window = TimeSpan.FromMinutes(1); // Time window
opt.PermitLimit = 3; // Maximum number of requests allowed in the time window
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; // Request processing order
opt.QueueLimit = 2; // Maximum number of requests allowed in the queue
});
});
return services;
}
public static IApplicationBuilder UseRateLimit(this IApplicationBuilder app) {
app.UseRateLimiter();
return app;
}
}
Here, the Fixed Window strategy is used for rate limiting (限流). Specific configurations are commented in the code.
PS: Regarding the four commonly used rate-limiting strategies (Fixed/Sliding Window, Token Bucket, Concurrency), they have been introduced in the previous article.
Next, add the following to the Program.cs file:
builder.Services.AddRateLimit();
Configure the middleware:
var app = builder.Build();
app.MapHealthChecks("/healthz");
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseExceptionless();
app.UseHttpsRedirection();
app.UseRouting(); // Routing should be defined before applying rate limiting
app.UseRateLimiter(); // Rate limiting should be applied after routing
app.UseCors(); // CORS should be applied after rate limiting
app.UseAuthentication(); // Authentication should be applied before authorization
app.UseAuthorization(); // Authorization should be applied after authentication
app.UseSwaggerWithAuthorize();
app.MapControllers();
app.Run();
Note: You must place the rate-limiting middleware immediately after UseRouting.
Since we need to add rate-limiting policies to specific endpoints, the request must first be routed to the designated endpoint before rate limiting is applied.
Adding Rate Limiting Configuration to Interfaces
To add rate limiting to a Controller or interface method that requires it, simply add [EnableRateLimiting(RateLimitPolicies.Fixed)].
For example, this login interface:
[HttpPost("login/password")]
[EnableRateLimiting(RateLimitPolicies.Fixed)]
[ValidateClient(ClientIdSource.Body, ParameterName = "ClientId")]
public async Task<IActionResult> LoginByPassword(PwdLoginDto dto) {}
Test Results
Test the API interface using curl in the shell.
for i in {1..11}; do curl http://localhost:5000/api/auth/login/password; done
With the configuration above, the rate-limiting policy should be triggered on the 11th request, causing it to hang until it returns after 1 minute.
This is because we configured opt.QueueLimit = 2, meaning that when rate limiting is triggered, 2 requests can be queued, and these 2 requests will wait until the system can regenerate a response.
If a 3rd request comes in, it will immediately return a 503 (Service Unavailable) status. The component returns 503 by default, but you can configure it to return 429 (Too many requests) instead.
Configuration
Configuring the Wait Queue
Setting opt.QueueLimit to 0 immediately rejects responses when rate limiting is triggered.
Custom Rejection Response
Previously, we mentioned that triggering rate limiting returns a 503 status. We can also configure it to return 429 ourselves.
services.AddRateLimiter(options => {
options.OnRejected = (context, token) => {
// 检查 context.Lease 中是否包含 RetryAfter 元数据
// RetryAfter 元数据通常指示客户端应该在多少秒后重试请求
if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter)) {
// 如果 RetryAfter 元数据存在,将 RetryAfter 值(以秒为单位)添加到响应头中
context.HttpContext.Response.Headers.RetryAfter =
((int)retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo);
}
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
context.HttpContext.Response.WriteAsync("Too many requests. Please try again later.",
cancellationToken: token);
return new ValueTask();
};
});
In fact, this is the approach shown in the official documentation. The most critical line is context.HttpContext.Response.StatusCode; the rest is just an added bonus and can be omitted.
Extensions
IP Address-Based Rate Limiting
Previously, when I used the AspNetCoreRateLimit component, I directly utilized its built-in IP address rate limiting functionality.
In the native Microsoft.AspNetCore.RateLimiting, this feature is not built-in and needs to be implemented manually.
I looked into the approach described in the documentation. The idea is to create a partition for each IP address (PartitionedRateLimiter.Create) and then configure the rate limiting strategy within each partition.
For details, refer to the limiter-with-onrejected-retryafter-and-globallimiter section in the official documentation.
This implementation approach also needs to consider that the application may be vulnerable to denial-of-service attacks using IP source address spoofing. See the reference materials for more details.
PS: It's a bit complicated. I might just go back to using AspNetCoreRateLimit. What a hassle.
Rate Limiting Based on Users (根据用户进行限流)
Through a custom policy, you can implement rate limiting based on logged-in users, for example, each user can only make 10 requests per minute.
For details, refer to the limiter-with-authorization section of the official documentation.
By the way, the official documentation also mentions the dotnet user-jwts tool. This is the first time I've heard of it, so I'll mark it to check it out later.
Summary
That's about it. After trying it out, I found it to be too much hassle, so I'm sticking with the original AspNetCoreRateLimit component.
Reference Materials
_EOF_
ASP.NET Core Development Notes: Using Native Rate Limiting for Interfaces
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
Asp-Net-Core开发笔记:使用alpine镜像并加入健康检查
用alpine镜像?不过是把体积砍半,健康检查加个路径就完事。wget自带,何必装curl,真是多余。发布选linux-musl-x64,不然别想跑。
2024-04-19 · Asp-Net-Core开发笔记 · 约 1 分钟
Asp-Net-Core开发笔记:FrameworkDependent搭配docker部署
之前我写过一篇使用 docker 部署 AspNetCore 应用的文章,这种方式搭配 CICD 非常方便, build 之后 push 到私有的 dockerhub ,在生产服务器上 pull 下来镜像就可以直接运行了。 然而,有时需要一种更传统的部署方式,比如在本地打包可执行文件之后直接放到服务器上执行。 这种方式配置完成之后,每次更新只需要重新本地重新 publish 后把可执行文件上传
2023-09-08 · Asp-Net-Core开发笔记 · 约 1 分钟
Asp-Net-Core开发笔记:使用ActionFilterAttribute实现非侵入式的参数校验
用ActionFilterAttribute搞非侵入校验,省得重复写代码。支持Query、Body、Route、Header,HttpContext共享数据,但OAuth2.0边学边做,项目乱糟糟,功能凑合。
2024-05-17 · Asp-Net-Core开发笔记 · 约 1 分钟
Asp-Net-Core开发笔记:使用NPM和gulp管理前端静态文件
前言 本文介绍的是AspNetCore的MVC项目,WebApi+独立前端这种前后端分离的项目就不需要多此一举了~默认前端小伙伴是懂得使用前端工具链的。 为啥要用MVC这种服务端渲染技术呢? 简单项目不需要强行分离增加复杂度(如:我正在开发的博客项目) 后端渲染利于SEO,对博客网站友好 OK,虽然MVC的技术老了点,但依然可以结合现代前端工具链来提高效率 本文的食用需要先安装好Node.js环境
2022-02-17 · Asp-Net-Core开发笔记 · 约 1 分钟
讨论
共 0 条加载评论中…