I am trying to learn using basic rate limiting in an ASP.NET Core 8.0 Web API. I set 'permit limit' to 5, 'window' to 1 minute and 'queue limit' of 2.
At this juncture, let me tell you that I have some services registered in my Program.cs; eg. for Swagger, for global exception handler, for working with Couchbase database, for adding Serilog logger and so forth.
Now, I am trying to test the default endpoint in WeatherForecastControl by applying [EnableRateLimiting("fixed")] to it. But it isn't working ideally ie as expected. The first 5 requests are returning the data as it should. The 6th request makes Swagger display a 'loading' spinner for an indefinite period. I didn't use timer or anything else so I don't know whether it's running for the 1 minute time-frame or not. Then it is fetching and showing the data again with a 200 OK status.
If my knowledge serves me right, the 6th request should rightaway return a 429 status code with a message. Right? I have a hunch, that it is all due to wrong placement of the AddFixedRateLimiter() and UseRateLimiter() in my Program.cs file.
Let me share some code - RateLimiterExtension.cs:
public static class RateLimiterExtension
{
public static IServiceCollection AddFixedRateLimiter(this IServiceCollection services)
{
services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("fixed", builder =>
{
builder.PermitLimit = 5; // Number of requests allowed
builder.Window = TimeSpan.FromMinutes(1); // Time window for the limit
builder.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; // Order of processing requests
builder.QueueLimit = 2; // Maximum number of requests in the queue
});
options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.StatusCode = 429;
await context.HttpContext.Response.WriteAsync("Rate limit exceeded!", token);
};
});
return services;
}
}
Program.cs:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddTransient<GlobalExceptionHandlingMiddleware>();
builder.Services.AddControllers();
builder.Services.AddSwaggerExplorer()
.InjectCouchbaseContext(builder.Configuration);
builder.Services.AddApplicationServices();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.CreateLogger();
builder.Services.AddFixedRateLimiter();
var app = builder.Build();
app.ConfigureSwaggerExplorer()
.UseHttpsRedirection()
.UseAuthorization();
app.UseMiddleware<GlobalExceptionHandlingMiddleware>();
app.UseRateLimiter();
app.MapControllers();
app.Lifetime.ApplicationStopped.Register(() =>
{
app.Services.GetRequiredService<ICouchbaseLifetimeService>().Close();
});
app.Run();
And finally in the controller:
[ApiController]
[Route("[controller]")]
[EnableRateLimiting("fixed")]
public class WeatherForecastController : ControllerBase
{
// ...
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
// ...
}
}
Please study my Program.cs code closely so that you can understand my approach and specially, placement of the AddFixedRateLimiter() and UseRateLimiter() methods. Do you think I have implemented this correctly? Why is Swagger showing the loading spinner instead of returning 429 response? Or I am entirely mistaken and this implementation is working just fine?
Kindly guide me in the right direction.
1 Answer 1
I managed to find the solution for this.
I removed the 2 lines for QueueProcessingOrder and QueueLimit from my rate limiting logic in RateLimiterExtension.cs file.
Also added app.UseRouting() to my Program.cs file.
I found out that the order matters too when configuring the pipeline. So I have put builder.Services.AddFixedRateLimiter() after all other service extensions are registered. ie. just before var app = builder.Build();
My rate limiting functionality now works as desired and returns 429 status code with the message when the number of HTTP requests is limited.
2 Comments
Explore related questions
See similar questions with these tags.
OnRejectedhandler get invoked at all? Does any server-side code get invoked? If so, what does it do from there while debugging?