zijian666.WebApi.RateLimitQuota 2.2.1-beta

This is a prerelease version of zijian666.WebApi.RateLimitQuota.
dotnet add package zijian666.WebApi.RateLimitQuota --version 2.2.1-beta
                    
NuGet\Install-Package zijian666.WebApi.RateLimitQuota -Version 2.2.1-beta
                    
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="zijian666.WebApi.RateLimitQuota" Version="2.2.1-beta" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="zijian666.WebApi.RateLimitQuota" Version="2.2.1-beta" />
                    
Directory.Packages.props
<PackageReference Include="zijian666.WebApi.RateLimitQuota" />
                    
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 zijian666.WebApi.RateLimitQuota --version 2.2.1-beta
                    
#r "nuget: zijian666.WebApi.RateLimitQuota, 2.2.1-beta"
                    
#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 zijian666.WebApi.RateLimitQuota@2.2.1-beta
                    
#: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=zijian666.WebApi.RateLimitQuota&version=2.2.1-beta&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=zijian666.WebApi.RateLimitQuota&version=2.2.1-beta&prerelease
                    
Install as a Cake Tool

RateLimitQuota 速率限制配额模块

概述

RateLimitQuota 是一个速率限制配额功能模块,用于在 ASP.NET Core 应用中限制接口访问频率,防止接口被过度调用。

核心特性

  • 灵活的配额提供者:支持多个配额提供者,可以为不同的接口或用户设置不同的限流规则
  • 可扩展的计数器:支持自定义速率计数器实现,可以使用内存、Redis 等存储方式
  • 自动重试提示:当触发限流时,自动在响应头中添加 Retry-After 提示客户端重试时间
  • 零配置使用:通过构建器模式轻松配置和使用

快速开始

1. 实现速率计数器

实现 IRateCounter 接口来定义如何计数:

using System.Collections.Concurrent;
using zijian666.WebApi.Abstractions;

public class MemoryRateCounter : IRateCounter
{
    private readonly ConcurrentDictionary<string, (int count, DateTime expire)> _cache = new();

    public ValueTask<int> Increment(string key, DateTime expire)
    {
        var (count, _) = _cache.AddOrUpdate(key, 
            _ => (1, expire), 
            (k, item) =>
            {
                var (currentCount, oldExpire) = item;
                // 如果已过期,重置计数
                if (oldExpire < DateTime.Now)
                {
                    return (1, expire);
                }
                return (currentCount + 1, oldExpire);
            });
        return ValueTask.FromResult(count);
    }

    public ValueTask<DateTime?> GetExpire(string key)
    {
        if (_cache.TryGetValue(key, out var value))
        {
            return ValueTask.FromResult<DateTime?>(value.expire);
        }
        return ValueTask.FromResult<DateTime?>(null);
    }
}

2. 实现配额提供者

实现 IRateLimitQuotaProvider 接口来定义限流规则:

using zijian666.WebApi.Abstractions;

public class DefaultRateLimitQuotaProvider : IRateLimitQuotaProvider
{
    public ValueTask<RateLimitQuota[]> Get(ApiActionContext context)
    {
        // 根据接口路径、用户等信息设置不同的限流规则
        var quota = new RateLimitQuota
        {
            Key = $"rate_limit:{context.ActionDescriptor.DisplayName}", // 使用接口路径作为键
            Limit = 10, // 限制为 10 次
            Expire = DateTime.Now.AddSeconds(60) // 60 秒内有效
        };
        return ValueTask.FromResult(new[] { quota });
    }
}

3. 注册服务

在 Program.cs 中注册速率限制配额功能:

using zijian666.WebApi;

var builder = WebApplication.CreateBuilder(args);

// 添加速率限制配额功能
builder.AddWebApi(webApi =>
{
    webApi.AddFeature<RateLimitQuotaBuilder>(rateLimit =>
    {
        // 注册速率计数器类型
        rateLimit.RateCounterType = typeof(MemoryRateCounter);
        
        // 添加配额提供者
        rateLimit.AddRateLimitQuotaProvider<DefaultRateLimitQuotaProvider>();
    });
});

var app = builder.Build();

// 使用速率限制配额(中间件会自动注册)
app.UseWebApi();

app.Run();

高级用法

多个配额提供者

支持注册多个配额提供者,系统会依次检查所有配额:

rateLimit.AddRateLimitQuotaProvider<DefaultRateLimitQuotaProvider>();
rateLimit.AddRateLimitQuotaProvider<UserBasedRateLimitQuotaProvider>(); // 基于用户的限流
rateLimit.AddRateLimitQuotaProvider<ApiBasedRateLimitQuotaProvider>(); // 基于接口的限流

基于用户的限流

public class UserBasedRateLimitQuotaProvider : IRateLimitQuotaProvider
{
    public ValueTask<RateLimitQuota[]> Get(ApiActionContext context)
    {
        var userId = context.HttpContext.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (string.IsNullOrEmpty(userId))
        {
            return ValueTask.FromResult(Array.Empty<RateLimitQuota>());
        }

        var quota = new RateLimitQuota
        {
            Key = $"user_rate_limit:{userId}",
            Limit = 100, // 每个用户限制 100 次
            Expire = DateTime.Now.AddHours(1) // 1 小时内有效
        };
        return ValueTask.FromResult(new[] { quota });
    }
}

基于接口的限流

public class ApiBasedRateLimitQuotaProvider : IRateLimitQuotaProvider
{
    public ValueTask<RateLimitQuota[]> Get(ApiActionContext context)
    {
        var route = context.ActionDescriptor.AttributeRouteInfo?.Template;
        if (string.IsNullOrEmpty(route))
        {
            return ValueTask.FromResult(Array.Empty<RateLimitQuota>());
        }

        // 为不同的接口设置不同的限流规则
        var limit = route.StartsWith("/api/admin/") ? 5 : 20;
        
        var quota = new RateLimitQuota
        {
            Key = $"api_rate_limit:{route}",
            Limit = limit,
            Expire = DateTime.Now.AddMinutes(1) // 1 分钟内有效
        };
        return ValueTask.FromResult(new[] { quota });
    }
}

使用 Redis 作为计数器存储

using StackExchange.Redis;

public class RedisRateCounter : IRateCounter
{
    private readonly IDatabase _database;

    public RedisRateCounter(IConnectionMultiplexer redis)
    {
        _database = redis.GetDatabase();
    }

    public async ValueTask<int> Increment(string key, DateTime expire)
    {
        var count = await _database.StringIncrementAsync(key);
        
        // 设置过期时间
        if (count == 1)
        {
            await _database.KeyExpireAsync(key, expire - DateTime.Now);
        }
        
        return (int)count;
    }

    public async ValueTask<DateTime?> GetExpire(string key)
    {
        var ttl = await _database.KeyTimeToLiveAsync(key);
        if (ttl.HasValue)
        {
            return DateTime.Now.Add(ttl.Value);
        }
        return null;
    }
}

API 参考

RateLimitQuotaBuilder

属性
  • RateCounterType:速率计数器类型,必须实现 IRateCounter 接口
方法
  • AddRateLimitQuotaProvider<T>():添加一个配额提供者(泛型版本)
  • AddRateLimitQuotaProvider(Type type):添加一个配额提供者(类型版本)

IRateCounter

方法
  • Increment(string key, DateTime expire):对指定的键进行计数,并设置过期时间,返回当前计数
  • GetExpire(string key):获取指定键的过期时间

IRateLimitQuotaProvider

方法
  • Get(ApiActionContext context):根据上下文获取限流配额数组

RateLimitQuota

属性
  • Key:资源键,用于标识限流资源
  • Limit:限制频率,超过此值将触发限流
  • Expire:过期时间,超过此时间计数将重置

RateLimitQuotaException

当触发限流时,会抛出 RateLimitQuotaException 异常,HTTP 状态码为 429 (Too Many Requests)。

工作原理

  1. 服务注册阶段(ConfigureServices):

    • 注册速率计数器类型(如果指定)
    • 注册配额提供者
    • 注册中间件为单例服务
  2. 中间件处理阶段(Use):

    • 获取所有注册的配额提供者
    • 获取速率计数器实例
    • 对每个配额提供者获取限流配额
    • 对每个配额进行计数检查
    • 如果超过限制,抛出 RateLimitQuotaException 并在响应头中添加 Retry-After
  3. 异常处理:

    • RateLimitQuotaException 会被异常处理中间件捕获
    • 返回 HTTP 429 状态码
    • 响应头中包含 Retry-After 提示客户端重试时间

最佳实践

  1. 选择合适的计数器存储:

    • 单机应用:使用内存计数器(如 MemoryRateCounter)
    • 分布式应用:使用 Redis 等分布式缓存作为计数器存储
  2. 合理设置限流规则:

    • 根据接口的重要性和性能要求设置不同的限流阈值
    • 考虑用户类型(普通用户、VIP 用户等)设置不同的限流规则
  3. 配额键的设计:

    • 使用有意义的键名,便于调试和监控
    • 考虑使用组合键(如 user:{userId}:api:{route})来区分不同的限流维度
  4. 错误处理:

    • 确保 IRateCounter 实现是线程安全的
    • 在计数器实现中妥善处理异常情况

注意事项

  • 必须注册至少一个 IRateCounter 实现,否则会抛出异常
  • 必须注册至少一个 IRateLimitQuotaProvider 实现,否则限流功能不会生效
  • RateLimitQuotaException 的 StackTrace 属性返回 null,以减少异常信息泄露
  • 限流检查在中间件管道中执行,确保在路由和认证之后执行

示例项目

更多使用示例请参考:

  • example/WebApiDemo8:基础限流示例
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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 (1)

Showing the top 1 NuGet packages that depend on zijian666.WebApi.RateLimitQuota:

Package Downloads
zijian666.WebApi

用于快速创建简单易用的标准化WebApi项目

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.2.1-beta 101 5/31/2026

UPLOGS.md