Rate Limit .NET 6

O que é
Beleza vamos começar com o que é ratelimit, na real não tem muito que falar, é basicamente o limite de requisições que podem ser feitas para um endpoint em um determinado período de tempo.
Porque usar
Então vamos direto ao ponto, quando qualquer um faz um endpoint tem algumas coisas que passam batido, e o ratelimit é uma delas, o que isso implica, caso você tenha um ataque ddos não vai ter nada que impeça esse ataque de sugar tudo que ele pode dos seus recursos, também conhecido como seu dinheiro. sem falar de derrubar o seu sistema o que pode ser até pior, o ratelimit é uma barreira para isso.
Como funciona
Como funciona basicamente você cria um middleware que vai ser executado toda vez antes das suas requisições, validar quantas requisições foram feitas para aquela rota, caso esteja dentro do limite em um período, ai ele executa o seu endpoint. Ele tem que ser executado antes para não consumir os recursos e depois validar, feito isso ele devolve alguns Headers que você pode colocar ou não indicando quantas requisições tem, quantas foram gastas e quanto tempo para encerrar esse período. O que não pode faltar é uma mensagem de erro que indica que você bateu no rate limit.
Como implementar
Primeiramente vamos criar uma classe:
public class RateLimitingMiddleware
{
private readonly RequestDelegate _next;
private readonly int _maxRequests;
private readonly int _timeWindowSeconds;
private const string COOKIE_NAME = "RateLimit";
public RateLimitingMiddleware(RequestDelegate next, IConfiguration configuration)
{
_next = next;
_maxRequests = int.TryParse(configuration["RateLimiting:MaxRequests"], out var max) ? max : 10;
_timeWindowSeconds = int.TryParse(configuration["RateLimiting:TimeWindowSeconds"], out var time) ? time : 60;
}
public async Task InvokeAsync(HttpContext context)
{
var now = DateTime.UtcNow;
var requestTimes = new List<DateTime>();
try
{
if (context.Request.Cookies.TryGetValue(COOKIE_NAME, out var cookieValue) && cookieValue.Length < 2000)
{
var timestamps = cookieValue.Split(',');
foreach (var ts in timestamps)
{
if (long.TryParse(ts, out long ticks))
{
var time = new DateTime(ticks, DateTimeKind.Utc);
if (time > now.AddSeconds(-_timeWindowSeconds))
requestTimes.Add(time);
}
}
}
requestTimes.Add(now);
requestTimes.RemoveAll(t => t < now.AddSeconds(-_timeWindowSeconds));
int remaining = Math.Max(0, _maxRequests - requestTimes.Count);
DateTime resetTime = requestTimes.FirstOrDefault().AddSeconds(_timeWindowSeconds);
int secondsToReset = (int)Math.Ceiling((resetTime - now).TotalSeconds);
context.Response.Headers["X-RateLimit-Limit"] = _maxRequests.ToString();
context.Response.Headers["X-RateLimit-Remaining"] = remaining.ToString();
context.Response.Headers["X-RateLimit-Reset"] = secondsToReset.ToString();
if (requestTimes.Count > _maxRequests)
{
context.Response.StatusCode = 429;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync("{\"error\":\"Rate limit exceeded. Try again later.\"}");
return;
}
var cookieOptions = new CookieOptions
{
HttpOnly = true,
Secure = context.Request.IsHttps,
Expires = now.AddSeconds(_timeWindowSeconds),
SameSite = SameSiteMode.Lax
};
var newValue = string.Join(",", requestTimes.Select(t => t.Ticks.ToString()));
context.Response.Cookies.Append(COOKIE_NAME, newValue, cookieOptions);
await _next(context);
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
await context.Response.WriteAsync("Internal Server Error in RateLimiter");
}
}
}
Configurar no program:
app.UseMiddleware<RateLimitingMiddleware>();
Por enquanto é isso com o tempo eu vou completando mais esse artigo, já tenho umas ideias.
Segue aqui as referencias:
https://learn.microsoft.com/pt-br/aspnet/core/fundamentals/middleware/?view=aspnetcore-9.0
https://www.redhat.com/pt-br/topics/middleware/what-is-middleware
https://aws.amazon.com/pt/what-is/middleware/
https://medium.com/@osama94/rate-limiting-in-net-6-8d8d17e4d2d1
Subscribe to my newsletter
Read articles from Henrique Ameixa directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
