Wiaoj.RateLimiting.AspNetCore 0.0.1-alpha.99-preview

This is a prerelease version of Wiaoj.RateLimiting.AspNetCore.
dotnet add package Wiaoj.RateLimiting.AspNetCore --version 0.0.1-alpha.99-preview
                    
NuGet\Install-Package Wiaoj.RateLimiting.AspNetCore -Version 0.0.1-alpha.99-preview
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Wiaoj.RateLimiting.AspNetCore" Version="0.0.1-alpha.99-preview" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Wiaoj.RateLimiting.AspNetCore" Version="0.0.1-alpha.99-preview" />
                    
Directory.Packages.props
<PackageReference Include="Wiaoj.RateLimiting.AspNetCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Wiaoj.RateLimiting.AspNetCore --version 0.0.1-alpha.99-preview
                    
#r "nuget: Wiaoj.RateLimiting.AspNetCore, 0.0.1-alpha.99-preview"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Wiaoj.RateLimiting.AspNetCore@0.0.1-alpha.99-preview
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Wiaoj.RateLimiting.AspNetCore&version=0.0.1-alpha.99-preview&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Wiaoj.RateLimiting.AspNetCore&version=0.0.1-alpha.99-preview&prerelease
                    
Install as a Cake Tool

Wiaoj.RateLimiting.AspNetCore

ASP.NET Core integration package for Wiaoj.RateLimiting, providing HTTP middleware, partition key extractors, endpoint metadata conventions, and RFC-compliant response handling.


Installation

dotnet add package Wiaoj.RateLimiting.AspNetCore

What This Package Contains

  • RateLimitingMiddleware: ASP.NET Core middleware that evaluates incoming requests against configured named or default rate limiting policies, applies endpoint costs, and emits RFC-standard responses.
  • Key Selectors (IRateLimitKeySelector):
    • ClientIpKeySelector: Formats remote IPv4/IPv6 addresses directly using stack-allocated buffers.
    • ApiKeyHeaderKeySelector: Extracts identity keys from configurable request headers (e.g. X-Api-Key), with configurable fallback selectors.
    • UserClaimKeySelector: Extracts identity keys from authenticated claims (ClaimsPrincipal), with configurable fallback selectors.
  • Endpoint Metadata and Route Conventions:
    • [DisableRateLimiting] / .DisableRateLimiting(): Bypasses rate limiting for targeted endpoints.
    • [RateLimitCost(int)] / .WithRateLimitCost(int): Statically overrides the quota cost for an endpoint.
    • .WithRateLimitCost(Func<HttpContext, int>): Dynamically computes quota cost at runtime (e.g. from batch payload counts or query parameters).
  • RFC and Standards Compliance:
    • RFC 6585: Emits HTTP 429 Too Many Requests.
    • RFC 9110: Emits integer delta-seconds Retry-After header.
    • RFC 7807 / RFC 9457: Serializes application/problem+json ProblemDetails responses.
    • IETF Draft Headers: Writes RateLimit-Remaining and RateLimit-Reset headers.

Setup and Configuration

1. Register Services in Program.cs

using Wiaoj.DistributedCounter;
using Wiaoj.RateLimiting;
using Wiaoj.RateLimiting.AspNetCore;
using Wiaoj.RateLimiting.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// Configure counter backend
builder.Services.AddDistributedCounter(dc => dc.UseInMemory());

// Configure rate limiting policies and ASP.NET Core options
builder.Services.AddWiaojRateLimiting(limiter => {
    limiter.AddPolicy("auth", policy => policy.UseFixedWindow(limit: 5, window: TimeSpan.FromMinutes(1)));
    limiter.UseDefaultPolicy(policy => policy.UseSlidingWindow(limit: 100, window: TimeSpan.FromMinutes(1)));

    limiter.WithAspNetCore(options => {
        options.KeySelector = new ClientIpKeySelector(prefix: "ip:");
        options.StatusCode = StatusCodes.Status429TooManyRequests;
        options.EnableIetfHeaders = true;
        options.UseProblemDetails = true;
    });
});

var app = builder.Build();

// Enable the middleware in the request pipeline
app.UseWiaojRateLimiting();

Endpoint Routing Conventions

Static Costing, Dynamic Costing, and Exemptions

// 1. Endpoint with static custom cost (consumes 5 quota units per call)
app.MapPost("/api/export", () => Results.Ok())
   .WithRateLimitCost(5);

// 2. Endpoint with dynamic cost calculated from query parameters
app.MapPost("/api/batch", (int batchSize) => Results.Ok())
   .WithRateLimitCost(ctx => {
       if (ctx.Request.Query.TryGetValue("batchSize", out var val) && int.TryParse(val, out int count)) {
           return Math.Max(1, count);
       }
       return 1;
   });

// 3. Endpoint exempt from all rate limiting
app.MapGet("/healthz", () => Results.Ok("OK"))
   .DisableRateLimiting();

Key Selectors

Key selectors define how client identity is extracted from HttpContext:

limiter.WithAspNetCore(options => {
    // 1. Client IP address (IPv4 / IPv6)
    options.KeySelector = new ClientIpKeySelector(prefix: "ip:");

    // 2. API Key header with fallback to IP if missing
    options.KeySelector = new ApiKeyHeaderKeySelector(
        headerName: "X-Api-Key",
        prefix: "api_key:",
        fallbackSelector: new ClientIpKeySelector("anon_ip:"));

    // 3. User claim (Subject ID) with fallback to IP if unauthenticated
    options.KeySelector = new UserClaimKeySelector(
        claimType: ClaimTypes.NameIdentifier,
        prefix: "user:",
        fallbackSelector: new ClientIpKeySelector("anon_ip:"));
});

Response Formatting & ProblemDetails

When a request is rejected, the middleware formats the response according to configured options:

Standard RFC 7807 Response Body (application/problem+json)

{
  "type": "https://tools.ietf.org/html/rfc6585#section-4",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Rate limit exceeded. Quota will be available in 12 seconds.",
  "instance": "/api/orders",
  "retryAfter": 12,
  "remaining": 0
}

Customizing ProblemDetails

limiter.WithAspNetCore(options => {
    options.UseProblemDetails = true;
    options.ProblemDetailsCustomizer = (problem, context, decision) => {
        problem.Extensions["traceId"] = context.TraceIdentifier;
        problem.Extensions["policy"] = "auth";
    };
});

Custom Low-Level Rejection Callback

limiter.WithAspNetCore(options => {
    options.OnRejectedAsync = (context, decision) => {
        context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
        return context.Response.WriteAsync("Custom plain-text rate limit rejection.");
    };
});

License

This project is licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.1-alpha.99-preview 57 9/6/2026
0.0.1-alpha.98-preview 53 9/6/2026
0.0.1-alpha.97-preview 52 9/6/2026
0.0.1-alpha.96-preview 49 9/6/2026
0.0.1-alpha.95-preview 51 9/6/2026
0.0.1-alpha.109-preview 0 9/11/2026
0.0.1-alpha.108-preview 46 9/8/2026
0.0.1-alpha.107-preview 48 9/8/2026
0.0.1-alpha.106-preview 43 9/8/2026
0.0.1-alpha.105-preview 39 9/8/2026
0.0.1-alpha.104-preview 59 9/7/2026
0.0.1-alpha.103-preview 47 9/7/2026
0.0.1-alpha.102-preview 53 9/6/2026
0.0.1-alpha.101-preview 120 9/6/2026
0.0.1-alpha.100-preview 54 9/6/2026