Documentation Index
Fetch the complete documentation index at: https://mintlify.com/nimanikoo/Dotnet-RateLimiter/llms.txt
Use this file to discover all available pages before exploring further.
AddCustomRateLimiter is a single extension method that wires up every service Dotnet-RateLimiter needs in one call. It connects to Redis, registers the RedisRateLimiter singleton, configures all four built-in ASP.NET Core rate limiting policies, sets up health checks with a live UI dashboard, and registers controllers with Swagger — so your Program.cs stays clean and focused on the middleware pipeline.
NuGet Dependencies
Add the following packages to your project (versions match those declared in Dotnet-RateLimiter.csproj):
| Package | Version |
|---|
StackExchange.Redis | 2.10.1 |
AspNetCore.HealthChecks.Redis | 9.0.0 |
AspNetCore.HealthChecks.UI | 9.0.0 |
AspNetCore.HealthChecks.UI.Client | 9.0.0 |
AspNetCore.HealthChecks.UI.InMemory.Storage | 9.0.0 |
Swashbuckle.AspNetCore | 10.1.0 |
Microsoft.AspNetCore.OpenApi | 10.0.1 |
Install them via the .NET CLI:
dotnet add package StackExchange.Redis --version 2.10.1
dotnet add package AspNetCore.HealthChecks.Redis --version 9.0.0
dotnet add package AspNetCore.HealthChecks.UI --version 9.0.0
dotnet add package AspNetCore.HealthChecks.UI.Client --version 9.0.0
dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage --version 9.0.0
dotnet add package Swashbuckle.AspNetCore --version 10.1.0
dotnet add package Microsoft.AspNetCore.OpenApi --version 10.0.1
The AddCustomRateLimiter Extension Method
AddCustomRateLimiter lives in Extensions/RateLimiterExtensions.cs. Call it once from Program.cs and pass in IConfiguration so it can read the Redis connection string from appsettings.json.
using System.Threading.RateLimiting;
using Dotnet_RateLimiter.Services;
using Microsoft.AspNetCore.RateLimiting;
using StackExchange.Redis;
namespace Dotnet_RateLimiter.Extensions;
public static class RateLimiterExtensions
{
public static IServiceCollection AddCustomRateLimiter(this IServiceCollection services,
IConfiguration configuration)
{
var redisConnectionString = configuration.GetSection("Redis:ConnectionString").Value
?? "localhost:6379";
var redis = ConnectionMultiplexer.Connect(redisConnectionString);
services.AddSingleton<IConnectionMultiplexer>(redis);
services.AddHealthChecks()
.AddRedis(
redisConnectionString: configuration.GetSection("Redis:ConnectionString").Value!,
name: "Redis Server",
tags: ["db", "cache", "redis"]
);
services.AddHealthChecksUI(settings =>
{
settings.AddHealthCheckEndpoint("Main App", "http://localhost:8080/health");
settings.SetEvaluationTimeInSeconds(5);
}).AddInMemoryStorage();
services.AddSingleton<RedisRateLimiter>();
services.AddControllers();
services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("FixedWindowPolicy", opt =>
{
opt.Window = TimeSpan.FromSeconds(5);
opt.PermitLimit = 5;
opt.QueueLimit = 10;
//LIFO approach : Last in First out
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
}).RejectionStatusCode = 429;
options.AddSlidingWindowLimiter("SlidingWindowPolicy", opt =>
{
opt.Window = TimeSpan.FromSeconds(10);
opt.PermitLimit = 5;
opt.QueueLimit = 10;
//LIFO approach : Last in First out
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
opt.SegmentsPerWindow = 4;
}).RejectionStatusCode = 429;
options.AddConcurrencyLimiter("ConcurrencyPolicy", opt =>
{
opt.PermitLimit = 7;
opt.QueueLimit = 3;
//LIFO approach : Last in First out
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
}).RejectionStatusCode = 429;
options.AddTokenBucketLimiter("BucketPolicy", opt =>
{
opt.TokenLimit = 5;
opt.QueueLimit = 2;
//LIFO approach : Last in First out
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
opt.ReplenishmentPeriod = TimeSpan.FromSeconds(5);
opt.AutoReplenishment = true;
opt.TokensPerPeriod = 3;
}).RejectionStatusCode = 429;
});
return services;
}
}
What AddCustomRateLimiter registers
| Registration | Detail |
|---|
IConnectionMultiplexer (singleton) | Redis connection via StackExchange.Redis |
| Health checks | Redis probe named "Redis Server" with tags db, cache, redis |
| Health Checks UI | Dashboard polled every 5 seconds, backed by in-memory storage |
RedisRateLimiter (singleton) | Custom distributed rate limiter service |
| Controllers + Swagger | AddControllers, AddEndpointsApiExplorer, AddSwaggerGen |
FixedWindowPolicy | 5 requests per 5-second window, queue 10, rejection → 429 |
SlidingWindowPolicy | 5 requests per 10-second window, 4 segments, queue 10, rejection → 429 |
ConcurrencyPolicy | 7 concurrent requests, queue 3, rejection → 429 |
BucketPolicy | Token bucket: 5 tokens max, 3 tokens per 5-second period, auto-replenish, queue 2, rejection → 429 |
Middleware Pipeline (Program.cs)
The full Program.cs wires the middleware pipeline in a specific order that matters for correctness:
using Dotnet_RateLimiter.Extensions;
using Dotnet_RateLimiter.Middlewares;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddCustomRateLimiter(builder.Configuration);
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseRateLimiter(); // 1. Built-in ASP.NET Core policies
app.UseHttpsRedirection(); // 2. HTTPS redirect
app.UseAuthorization(); // 3. Authorization
app.UseMiddleware<RedisRateLimitingMiddleware>(); // 4. Custom Redis rate limiting
app.MapControllers(); // 5. Controller routes
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
}); // 6. Health check endpoint
app.UseHealthChecksUI(config =>
{
config.UIPath = "/health-ui";
}); // 7. Health Checks UI dashboard
app.Run();
Pipeline order explained
| Step | Middleware | Purpose |
|---|
| 1 | UseRateLimiter() | Applies in-memory [EnableRateLimiting("PolicyName")] attributes (FixedWindow, SlidingWindow, Concurrency, Bucket) |
| 2 | UseHttpsRedirection() | Redirects HTTP → HTTPS |
| 3 | UseAuthorization() | Resolves the authenticated user identity used by the Redis middleware |
| 4 | UseMiddleware<RedisRateLimitingMiddleware>() | Reads [RedisRateLimit] endpoint metadata and enforces distributed Redis-backed limits |
| 5 | MapControllers() | Registers controller action routes |
| 6 | MapHealthChecks("/health", ...) | Exposes the /health JSON endpoint consumed by the UI |
| 7 | UseHealthChecksUI(...) | Serves the live dashboard at /health-ui |
Middleware order is not optional. UseRateLimiter() must be called before UseMiddleware<RedisRateLimitingMiddleware>(). The built-in rate limiter needs to run first so that requests blocked by in-memory policies never reach the Redis layer. Additionally, UseAuthorization() must come before UseMiddleware<RedisRateLimitingMiddleware>() so that context.User is populated when the Redis middleware determines whether the caller is an authenticated user or an anonymous guest.