DotNet.RateLimit
A Distributed RateLimit for Controller-Actions and Minimal API.
Install / Use
/learn @sa-es-ir/DotNet.RateLimitREADME
DotNetRateLimiter
This is a RateLimit that works with ActionFilters and EndPointFilters! An approach designed to control the request rate for a specific Action, Controller, or minimal endpoint. The idea behind this solution is to solve the middleware problem because the Middlewares affects all requests, but with filters, you can limit some of the critical endpoints.
Rate Limit uses In-Memory cache by default, but if you set up a Redis connection, it will use Redis. It is recommended to use Redis for checking the rate limit in distributed applications. By default, it limits the IP address, but you can set an identifier (default name is ClientId) in the request headers, and the header name is configurable.
How to add in DI
You can add RateLimit in Startup like this:
using DotNet.RateLimiter;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimitService(builder.Configuration);
How to use
You can see the Demo project to know how to use it in all scenarios, and also you can follow this article in Medium.
Simple usage
Using RateLimit without any parameters
[HttpGet("")]
[RateLimit(PeriodInSec = 60, Limit = 3)]
public IEnumerable<WeatherForecast> Get()
{
....
}
For MinimalAPI .NET 7+
app.MapGet("/weatherforecast", () =>
{
return Results.Ok();
})
.WithRateLimiter(options =>
{
options.PeriodInSec = 60;
options.Limit = 3;
});
Using Route parameters
[HttpGet("by-route/{id}")]
[RateLimit(PeriodInSec = 60, Limit = 3, RouteParams = "id")]
public IEnumerable<WeatherForecast> Get(int id)
{
....
}
For MinimalAPI .NET 7+
app.MapGet("/weatherforecast/{id}", (int id) =>
{
return Results.Ok();
})
.WithRateLimiter(options =>
{
options.PeriodInSec = 60;
options.Limit = 3;
options.RouteParams = "id";
});
Using Query parameters
[HttpGet("by-query/{id}")]
[RateLimit(PeriodInSec = 60, Limit = 3, RouteParams = "id", QueryParams = "name,family")]
public IEnumerable<WeatherForecast> Get(int id, string name, [FromQuery] List<string> family)
{
....
}
For MinimalAPI .NET 7+
app.MapGet("/weatherforecast/{id}", (int id, string name, string family) =>
{
return Results.Ok();
})
.WithRateLimiter(options =>
{
options.PeriodInSec = 60;
options.Limit = 2;
options.QueryParams = "name,family";
options.RouteParams = "id";
});
Using with Body parameters (Only for ActionFilters)
// body parameter only works on root parameters and does not work on nested parameters.
[HttpPut]
[RateLimit(PeriodInSec = 60, Limit = 3, BodyParams = "name" )]
public IActionResult Update([FromBody] BodyRequest request)
{
....
}
....
public class BodyRequest
{
public string Name { get; set; }
}
Using on Controller
//if Scope set to Controller then the rate limit works on all actions and no matter which actions call
//the default value is Action which means the rate limit checks each action separately
[RateLimit(Limit = 3, PeriodInSec = 60, Scope = RateLimitScope.Controller)]
public class RateLimitOnAllController : ControllerBase
{ .... }
Ignoring rate limit in case of use on Controller
[RateLimit(Limit = 3, PeriodInSec = 60, Scope = RateLimitScope.Controller)]
public class RateLimitOnAllController : ControllerBase
{
[HttpGet("")]
[IgnoreRateLimit]//ignore rate limit for this action
public IEnumerable<WeatherForecast> Get()
{
....
}
}
Using Existing Redis Connections
If your application already has Redis infrastructure, you can reuse existing connections instead of creating new ones:
using DotNet.RateLimiter;
using StackExchange.Redis;
var builder = WebApplication.CreateBuilder(args);
// Setup your existing Redis connection
var multiplexer = ConnectionMultiplexer.Connect("your-connection-string");
builder.Services.AddSingleton<IConnectionMultiplexer>(multiplexer);
// Use existing connection with rate limiting
builder.Services.AddRateLimitService(builder.Configuration, multiplexer);
// Or use existing database
var database = multiplexer.GetDatabase();
builder.Services.AddRateLimitService(builder.Configuration, database);
📖 Read more about using existing Redis connections
Custom configuration
RateLimitOption in appsettings.json
"RateLimitOption": {
"EnableRateLimit": true, //Optional: if set false rate limit will be disabled, default is true
"HttpStatusCode": 429, //Optional: default is 429
"ErrorMessage": "Rate limit Exceeded", //Optional: default is Rate limit Exceeded
"IpHeaderName": "X-Forwarded-For" //Optional: header name for getting the IP address, default is X-Forwarded-For
"RedisConnection": "127.0.0.1:6379", //Optional
"IpWhiteList": ["::1"], //Optional
"ClientIdentifier": "X-Client-Id" //Optional: for getting client id from the request header if this is present the rate limit won't use IP but ClientId
"ClientIdentifierWhiteList": ["test-client"] //Optional
}
Custom Response Structure
You can customize the JSON response structure when rate limiting is triggered by using the ResponseStructure property in your configuration. This allows you to match your API's response format standards.
Configuration:
"RateLimitOption": {
"HttpStatusCode": 429,
"ErrorMessage": "Rate limit Exceeded",
"ResponseStructure": "{\"error\": {\"message\": \"$(ErrorMessage)\", \"code\": $(HttpStatusCode)}}"
}
Supported Placeholders:
$(ErrorMessage)- Replaced with the configured error message$(HttpStatusCode)- Replaced with the HTTP status code number
Default Response (when ResponseStructure is not set):
{
"message": "Rate limit Exceeded",
"code": 429
}
Custom Response Example:
With the configuration above, when rate limiting is triggered, the response will be:
{
"error": {
"message": "Rate limit Exceeded",
"code": 429
}
}
More Examples:
Simple flat structure:
"ResponseStructure": "{\"message\": \"$(ErrorMessage)\", \"statusCode\": $(HttpStatusCode)}"
Nested structure with additional fields:
"ResponseStructure": "{\"success\": false, \"error\": {\"type\": \"RateLimitError\", \"message\": \"$(ErrorMessage)\", \"code\": $(HttpStatusCode)}}"
The feature is fully backward compatible - existing applications will continue to work without any changes.
Related Skills
node-connect
340.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
340.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.2kCommit, push, and open a PR
