Implementing API Request Rate Limiting in ASP.NET Core
Saurav Kumar
Full-Stack Developer | Siemens Healthineers | ex-Manhattan Associates (R&D) | VIT, Vellore
1. Why Implement API Request Rate Limiting?
Imagine your API is like a popular coffee shop. Most customers come in, order a coffee, and leave without a hitch. But then you have those customers who show up, order 50 lattes in one go, and slow everything down for everyone else. In the API world, these customers are the users who flood your endpoints with hundreds or thousands of requests per minute.
Rate limiting ensures that everyone gets their coffee (or API response) in a timely manner by putting a cap on how much any one customer can order at a time. It protects your API from being overwhelmed, helps prevent abuse, and ensures fair use of your services.
2. How Does Rate Limiting Work?
Rate limiting works by tracking the number of requests
For our example, we’ll use the client’s IP address as the identifier and limit them to a certain number of requests per minute. If they exceed this limit, we’ll return an HTTP 429 status code—Too Many Requests—and ask them to try again later.
3. Implementing Rate Limiting Middleware
Let’s dive into the code and build a custom middleware for rate limiting
Step 1: Create the Middleware Class
First, create a new class called RateLimitingMiddleware that will handle the logic for limiting requests.
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class RateLimitingMiddleware
{
private readonly RequestDelegate _next;
private static readonly ConcurrentDictionary<string, (DateTime, int)> _requestCounts = new ConcurrentDictionary<string, (DateTime, int)>();
private readonly int _maxRequests;
private readonly TimeSpan _timeWindow;
public RateLimitingMiddleware(RequestDelegate next, int maxRequests, TimeSpan timeWindow)
{
_next = next;
_maxRequests = maxRequests;
_timeWindow = timeWindow;
}
public async Task InvokeAsync(HttpContext context)
{
var clientIp = context.Connection.RemoteIpAddress.ToString();
var currentTime = DateTime.UtcNow;
if (_requestCounts.TryGetValue(clientIp, out var entry))
{
if (currentTime - entry.Item1 < _timeWindow)
{
if (entry.Item2 >= _maxRequests)
{
var retryAfter = _timeWindow - (currentTime - entry.Item1);
context.Response.StatusCode = 429; // Too Many Requests
context.Response.Headers["Retry-After"] = retryAfter.TotalSeconds.ToString();
await context.Response.WriteAsync($"Too many requests. Please try again in {retryAfter.TotalSeconds} seconds.");
return;
}
else
{
_requestCounts[clientIp] = (entry.Item1, entry.Item2 + 1);
}
}
else
{
_requestCounts[clientIp] = (currentTime, 1);
}
}
else
{
_requestCounts[clientIp] = (currentTime, 1);
}
await _next(context);
}
}
Explanation:
Step 2: Register the Middleware
Now, add this middleware to the request pipeline in the Startup.cs file.
领英推荐
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Register the rate limiting middleware
app.UseMiddleware<RateLimitingMiddleware>(maxRequests: 5, timeWindow: TimeSpan.FromMinutes(1));
// Other middleware components (e.g., routing, static files)
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Explanation:
4. Enhancing Rate Limiting Middleware
You can extend this middleware to handle more complex scenarios. For example:
Here’s an enhanced version that logs rate limiting events:
public async Task InvokeAsync(HttpContext context)
{
var clientIp = context.Connection.RemoteIpAddress.ToString();
var currentTime = DateTime.UtcNow;
if (_requestCounts.TryGetValue(clientIp, out var entry))
{
if (currentTime - entry.Item1 < _timeWindow)
{
if (entry.Item2 >= _maxRequests)
{
var retryAfter = _timeWindow - (currentTime - entry.Item1);
context.Response.StatusCode = 429;
context.Response.Headers["Retry-After"] = retryAfter.TotalSeconds.ToString();
Console.WriteLine($"Rate limit exceeded by {clientIp}. Retry after {retryAfter.TotalSeconds} seconds.");
await context.Response.WriteAsync($"Too many requests. Please try again in {retryAfter.TotalSeconds} seconds.");
return;
}
else
{
_requestCounts[clientIp] = (entry.Item1, entry.Item2 + 1);
}
}
else
{
_requestCounts[clientIp] = (currentTime, 1);
}
}
else
{
_requestCounts[clientIp] = (currentTime, 1);
}
await _next(context);
}
Explanation:
Conclusion: Keep Your API Healthy with Rate Limiting
API request rate limiting is an essential tool in the developer's arsenal to ensure that your APIs remain responsive and fair for all users. By implementing custom middleware for rate limiting in ASP.NET Core, you gain fine-grained control over how your API handles high traffic, preventing abuse and protecting your resources.
Whether you're managing a public API or an internal service, rate limiting helps you maintain a high quality of service
?? Further Learning: Dive deeper into middleware and other ASP.NET Core features in the official documentation.
?? Next in .NET Nuggets: We’ll continue exploring advanced ASP.NET Core features to help you optimize and secure your applications. Stay tuned!
?? Have you implemented rate limiting in your APIs? Share your experiences and strategies in the comments!
?? Stay tuned for more insightful .NET Nuggets!
#DotNetNuggets #ASPNETCore #Middleware #RateLimiting #CSharp #API #SoftwareDevelopment