Feedback

Zu viele API-Aufrufe? So begrenzt du Requests pro Client in .NET8

Sprache: C#

Dieses Snippet beantwortet die häufige Frage „Wie verhindere ich zu viele Requests auf meine .NET API?“. Es zeigt, wie man [b]Requests pro Client serverseitig begrenzt[/b] – direkt in .NET 8 Minimal APIs, ohne Reverse Proxy oder API-Gateway. [b]Was passiert nach Überschreiten des Limits?[/b] Sobald ein Client mehr als 5 Requests innerhalb von 10 Sekunden sendet, werden weitere Anfragen sofort abgelehnt. Die API antwortet automatisch mit H[b]TTP 429 (Too Many Requests)[/b]. Da QueueLimit = 0 gesetzt ist, werden Anfragen nicht gepuffert, sondern konsequent verworfen – ideal für klare Schutzmechanismen und vorhersagbares Verhalten.
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("per-client", limiter =>
    {
        limiter.Window = TimeSpan.FromSeconds(10);
        limiter.PermitLimit = 5;
        limiter.QueueLimit = 0;
    });
});

var app = builder.Build();

app.UseRateLimiter();

app.MapGet("/api/data", () => Results.Ok("Zugriff erlaubt"))
   .RequireRateLimiting("per-client");

app.Run();
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("per-client", limiter =>
    {
        limiter.Window = TimeSpan.FromSeconds(10);
        limiter.PermitLimit = 5;
        limiter.QueueLimit = 0;
    });
});

var app = builder.Build();

app.UseRateLimiter();

app.MapGet("/api/data", () => Results.Ok("Zugriff erlaubt"))
   .RequireRateLimiting("per-client");

app.Run();

2 Kommentare

  1. [b]Du verwendest noch kein .NET 8?[/b]

    So kannst du es in älteren .NET-Versionen (z. B. .NET 6/7) umsetzen – mit einem einfachen In-Memory Middleware-Ansatz (ohne externe Pakete):

    Code (ASP.NET Core 6/7)

    [code]using System.Collections.Concurrent;

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();

    var requests = new ConcurrentDictionary();
    var window = TimeSpan.FromSeconds(10);
    var limit = 5;

    app.Use(async (context, next) =>
    {
    var key = context.Connection.RemoteIpAddress?.ToString() ?? „unknown“;
    var now = DateTime.UtcNow;

    var entry = requests.GetOrAdd(key, _ => (0, now));

    if (now – entry.WindowStart > window)
    entry = (0, now);

    if (entry.Count >= limit)
    {
    context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
    return;
    }

    requests[key] = (entry.Count + 1, entry.WindowStart);
    await next();
    });

    app.MapGet(„/api/data“, () => Results.Ok(„Zugriff erlaubt“));

    app.Run();
    [/code]

    Auch hier gilt: Nach Überschreiten des Limits wird HTTP 429 zurückgegeben.
    Für produktive Szenarien (Cluster, Redis, Sliding Window) empfiehlt sich später ein dediziertes Rate-Limiting-Framework.

  2. [b]Für noch ältere .NET-Versionen: ASP.NET Web API 2 / .NET Framework 4.x[/b]

    So kannst du es in noch älteren .NET-Versionen (ASP.NET Web API / MVC auf .NET Framework) umsetzen:

    Code (ASP.NET Web API 2 / .NET Framework 4.x)

    [code]using System;
    using System.Collections.Concurrent;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http.Controllers;
    using System.Web.Http.Filters;

    public class RateLimitAttribute : ActionFilterAttribute
    {
    private static readonly ConcurrentDictionary Requests
    = new ConcurrentDictionary();

    public int PermitLimit { get; set; } = 5;
    public int WindowSeconds { get; set; } = 10;

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
    var key = GetClientKey(actionContext);
    var now = DateTime.UtcNow;
    var window = TimeSpan.FromSeconds(WindowSeconds);

    var entry = Requests.GetOrAdd(key, _ => (0, now));

    if (now – entry.WindowStart > window)
    entry = (0, now);

    if (entry.Count >= PermitLimit)
    {
    actionContext.Response = actionContext.Request.CreateResponse(
    (HttpStatusCode)429, „Too Many Requests“);
    return;
    }

    Requests[key] = (entry.Count + 1, entry.WindowStart);
    base.OnActionExecuting(actionContext);
    }

    private static string GetClientKey(HttpActionContext ctx)
    {
    // simpel: IP-Adresse; je nach Hosting/Proxy ggf. X-Forwarded-For auswerten
    var ip = ctx.Request.GetOwinContext()?.Request?.RemoteIpAddress;
    return string.IsNullOrWhiteSpace(ip) ? „unknown“ : ip;
    }
    }
    [/code]

    [b]Anwendung (Web API Controller)[/b]

    [code]using System.Web.Http;

    public class DataController : ApiController
    {
    [RateLimit(PermitLimit = 5, WindowSeconds = 10)]
    [HttpGet]
    public IHttpActionResult Get()
    {
    return Ok(„Zugriff erlaubt“);
    }
    }
    [/code]