Core API Rate Limiting
Without rate limiting, a single client could flood the BookStore API with thousands of requests per second — overloading the server, exhausting the database, and degrading service for all other users. Rate limiting restricts how many requests a client can make in a given time window and returns a 429 Too Many Requests response when the limit is exceeded.
What Is Rate Limiting?
Without Rate Limiting:
Client A sends 10,000 requests/min → Server crashes
With Rate Limiting:
Client A sends 10,000 requests/min
→ First 100 requests: 200 OK ✓
→ Request 101+: 429 Too Many Requests ✗
→ Wait 1 minute → limit resets
ASP.NET Core Built-in Rate Limiting (.NET 7+)
Starting from .NET 7, rate limiting is built into ASP.NET Core — no extra package is needed.
Step 1 – Register Rate Limiting in Program.cs
// Program.cs
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
builder.Services.AddRateLimiter(options =>
{
// Policy 1: Fixed window — 100 requests per minute for general endpoints
options.AddFixedWindowLimiter("GeneralPolicy", config =>
{
config.PermitLimit = 100;
config.Window = TimeSpan.FromMinutes(1);
config.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
config.QueueLimit = 5;
});
// Policy 2: Sliding window — 50 requests per minute for write operations
options.AddSlidingWindowLimiter("WritePolicy", config =>
{
config.PermitLimit = 50;
config.Window = TimeSpan.FromMinutes(1);
config.SegmentsPerWindow = 6; // checks every 10 seconds
config.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
config.QueueLimit = 2;
});
// Policy 3: Token bucket — allows short bursts but enforces average rate
options.AddTokenBucketLimiter("BurstPolicy", config =>
{
config.TokenLimit = 20; // max 20 tokens
config.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
config.QueueLimit = 5;
config.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
config.TokensPerPeriod = 5; // add 5 tokens every 10 seconds
config.AutoReplenishment = true;
});
// Custom response when rate limit is exceeded
options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.StatusCode = 429;
context.HttpContext.Response.ContentType = "application/json";
await context.HttpContext.Response.WriteAsJsonAsync(new
{
statusCode = 429,
message = "Too many requests. Please slow down and try again later.",
retryAfter = "60 seconds"
}, cancellationToken: token);
};
});
Step 2 – Apply Rate Limiting in the Middleware Pipeline
// Program.cs
var app = builder.Build();
app.UseMiddleware<ExceptionHandlingMiddleware>();
app.UseHttpsRedirection();
app.UseCors("BookStorePolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseRateLimiter(); // ← add rate limiting before controller mapping
app.MapControllers();
app.Run();
Step 3 – Apply Policies to Controllers and Actions
// Controllers/V2/BooksController.cs
using Microsoft.AspNetCore.RateLimiting;
[ApiController]
[Route("api/v2/[controller]")]
[EnableRateLimiting("GeneralPolicy")] // ← applies to entire controller
public class BooksController : ControllerBase
{
// GET /api/v2/books — uses GeneralPolicy (100 req/min)
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> GetAll() { ... }
// GET /api/v2/books/1 — uses GeneralPolicy (100 req/min)
[HttpGet("{id:int}")]
[AllowAnonymous]
public async Task<IActionResult> GetById(int id) { ... }
// POST /api/v2/books — stricter WritePolicy (50 req/min)
[HttpPost]
[Authorize(Roles = "Admin")]
[EnableRateLimiting("WritePolicy")] // ← overrides controller policy
public async Task<IActionResult> Create([FromBody] BookCreateDto dto) { ... }
// PUT /api/v2/books/1 — stricter WritePolicy
[HttpPut("{id:int}")]
[Authorize(Roles = "Admin")]
[EnableRateLimiting("WritePolicy")]
public async Task<IActionResult> Update(int id, [FromBody] BookUpdateDto dto) { ... }
// DELETE /api/v2/books/1 — strictest BurstPolicy (20 per burst)
[HttpDelete("{id:int}")]
[Authorize(Roles = "Admin")]
[EnableRateLimiting("BurstPolicy")]
public async Task<IActionResult> Delete(int id) { ... }
}
Rate Limiting Algorithms Compared
| Algorithm | How It Works | BookStore Use Case |
|---|---|---|
| Fixed Window | Counts requests in fixed time windows (0-60s, 60-120s). Resets at window boundary. | General GET endpoints (100/min) |
| Sliding Window | Counts requests over a rolling window. Smoother than fixed window. | Write operations (POST/PUT) |
| Token Bucket | Bucket refills at a fixed rate. Allows short bursts when bucket is full. | Admin delete operations |
| Concurrency | Limits simultaneous requests, not rate. | Expensive search operations |
Rate Limit Exceeded Response
POST /api/v2/books (101st request in 1 minute)
Response: 429 Too Many Requests
Retry-After: 60
{
"statusCode": 429,
"message": "Too many requests. Please slow down and try again later.",
"retryAfter": "60 seconds"
}
Per-User Rate Limiting
The examples above limit requests per IP address. For authenticated users, rate limits can be set per user ID from the JWT token:
// Program.cs — per-user rate limiting based on JWT claims
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("PerUserPolicy", config =>
{
config.PermitLimit = 200;
config.Window = TimeSpan.FromMinutes(1);
});
// Partition the rate limit by user ID claim
options.AddPolicy("AuthenticatedUserPolicy", httpContext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpContext.User.FindFirst("Id")?.Value ?? httpContext.Connection.RemoteIpAddress?.ToString() ?? "anonymous",
factory: _ => new FixedWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1)
}));
});
Disabling Rate Limiting on Specific Endpoints
[HttpGet("health")]
[DisableRateLimiting] // ← health check endpoint, no limit needed
public IActionResult Health()
{
return Ok(new { status = "Healthy", timestamp = DateTime.UtcNow });
}
Rate Limiting Strategy for BookStore API
Endpoint Policy Limit
────────────────────────────────────────────────────────────
GET /api/v2/books GeneralPolicy 100 req/min
GET /api/v2/books/{id} GeneralPolicy 100 req/min
POST /api/v2/books WritePolicy 50 req/min
PUT /api/v2/books/{id} WritePolicy 50 req/min
DELETE /api/v2/books/{id} BurstPolicy 20 per burst
POST /api/auth/login WritePolicy 50 req/min (prevent brute force)
GET /health No limit Unlimited
Key Points
- Rate limiting prevents abuse and protects the server from being overwhelmed by too many requests.
- ASP.NET Core .NET 7+ has built-in rate limiting with four algorithms: Fixed Window, Sliding Window, Token Bucket, and Concurrency.
- Different policies can be applied to different endpoints — stricter limits on write operations, more lenient on reads.
- When a limit is exceeded, a 429 Too Many Requests response is returned with a
Retry-Afterheader. - Rate limiting can partition by IP address (anonymous users) or by user ID (authenticated users).
