Token导航 LogoToken导航TokenDH.com
开发需要联网github未标认证来源可访问许可证需确认审计通过

rate-limiting速率限制

Agent Skill

rate-limiting 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

329

周安装

14

GitHub Stars

15

下载量

115
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:rate-limiting(速率限制)
来源仓库:https://github.com/wshaddix/dotnet-skills
仓库路径:skills/rate-limiting
安装命令:
npx skills add https://github.com/wshaddix/dotnet-skills --skill rate-limiting
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/wshaddix/dotnet-skills --skill rate-limiting

简介

rate-limiting 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息。

  • 适合在 Codex、Claude、Cursor、Gemini CLI 中围绕仓库状态、代码变更或协作事项进行整理。
  • 通过 npx skills add 命令从指定 GitHub 仓库安装并使用。
  • 安装前需确认权限范围、维护状态,避免触发联网或文件读写操作。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

Rationale

Rate limiting protects applications from abuse, ensures fair resource usage, and prevents cascading failures during traffic spikes. Without proper rate limiting, APIs can be overwhelmed by malicious or accidental high-volume requests, leading to degraded performance or outages. These patterns provide production-ready approaches to request throttling in ASP.NET Core applications.

Patterns

Pattern 1: Built-in Rate Limiting Middleware (.NET 7+)

Use the built-in Microsoft.AspNetCore.RateLimiting middleware for common scenarios.

// Program.cs - Basic rate limiting configuration
builder.Services.AddRateLimiter(options =>
{
    // Global rate limit for all requests
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(
        httpContext =>
        {
            var clientId = httpContext.User.Identity?.Name ??
                          httpContext.Connection.RemoteIpAddress?.ToString() ??
                          "anonymous";

            return RateLimitPartition.GetFixedWindowLimiter(
                partitionKey: clientId,
                factory: _ => new FixedWindowRateLimiterOptions
                {
                    PermitLimit = 100,
                    Window = TimeSpan.FromMinutes(1),
                    QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
                    QueueLimit = 2
                });
        });

    // Named policies for different endpoints
    options.AddFixedWindowLimiter("login", opt =>
    {
        opt.PermitLimit = 5;
        opt.Window = TimeSpan.FromMinutes(5);
        opt.QueueLimit = 0; // Don't queue login requests
    });

    options.AddFixedWindowLimiter("api", opt =>
    {
        opt.PermitLimit = 1000;
        opt.Window = TimeSpan.FromMinutes(1);
    });

    options.AddSlidingWindowLimiter("strict", opt =>
    {
        opt.PermitLimit = 10;
        opt.Window = TimeSpan.FromSeconds(10);
        opt.SegmentsPerWindow = 2;
    });

    options.AddTokenBucketLimiter("burst", opt =>
    {
        opt.TokenLimit = 100;
        opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        opt.QueueLimit = 5;
        opt.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
        opt.TokensPerPeriod = 20;
        opt.AutoReplenishment = true;
    });

    options.AddConcurrencyLimiter("concurrent", opt =>
    {
        opt.PermitLimit = 10;
        opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        opt.QueueLimit = 5;
    });

    // Custom rejection response
    options.OnRejected = async (context, token) =>
    {
        context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
        context.HttpContext.Response.Headers.Append("Retry-After", "60");

        await context.HttpContext.Response.WriteAsJsonAsync(new
        {
            Error = "Rate limit exceeded. Please try again later.",
            RetryAfter = 60
        }, token);
    };
});

// Middleware placement (must be after UseRouting, before UseEndpoints)
var app = builder.Build();
app.UseRouting();
app.UseRateLimiter(); // Enable rate limiting
app.MapControllers();
app.MapRazorPages();

Pattern 2: Per-Endpoint Rate Limiting

Apply different rate limits to different endpoints using attributes or endpoint configuration.

// Using EnableRateLimiting attribute on controllers
[ApiController]
[Route("api/[controller]")]
[EnableRateLimiting("api")] // Use named policy
public class ProductsController : ControllerBase
{
    [HttpGet]
    public async Task<IActionResult> GetAll()
    {
        // Limited by "api" policy (1000 requests/minute)
        return Ok();
    }

    [HttpPost]
    [EnableRateLimiting("strict")] // Override with stricter policy
    public async Task<IActionResult> Create([FromBody] ProductDto dto)
    {
        // Limited by "strict" policy (10 requests/10 seconds)
        return Created();
    }
}

// Razor Pages with rate limiting
public class LoginModel : PageModel
{
    // Page is rate limited via attribute
    [RateLimitPolicy("login")]
    public async Task<IActionResult> OnPostAsync()
    {
        // Login logic - protected by login policy (5 attempts per 5 minutes)
    }
}

// Endpoint-specific configuration in Program.cs
app.MapPost("/api/login", async (LoginRequest request) =>
{
    // Login logic
})
.AddEndpointFilter<RateLimitEndpointFilter>()
.RequireRateLimiting("login");

// Disable rate limiting for specific endpoints
app.MapGet("/health", () => Results.Ok())
    .DisableRateLimiting();

Pattern 3: Redis-Based Distributed Rate Limiting

Use Redis for rate limiting in distributed/multi-server environments.

// Redis rate limiting configuration
builder.Services.AddRateLimiter(options =>
{
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(
        httpContext =>
        {
            var clientId = GetClientIdentifier(httpContext);

            return RateLimitPartition.GetFixedWindowLimiter(
                partitionKey: clientId,
                factory: partitionKey => new FixedWindowRateLimiterOptions
                {
                    PermitLimit = 100,
                    Window = TimeSpan.FromMinutes(1)
                });
        });
});

// Custom distributed rate limiter using Redis
public class RedisRateLimiter : IRateLimiter
{
    private readonly IConnectionMultiplexer _redis;
    private readonly ILogger<RedisRateLimiter> _logger;

    public RedisRateLimiter(IConnectionMultiplexer redis, ILogger<RedisRateLimiter> logger)
    {
        _redis = redis;
        _logger = logger;
    }

    public async Task<RateLimitResult> CheckLimitAsync(
        string key,
        int limit,
        TimeSpan window)
    {
        var db = _redis.GetDatabase();
        var redisKey = $"ratelimit:{key}";

        // Lua script for atomic check-and-increment
        var script = @"
            local current = redis.call('GET', KEYS[1])
            if current == false then
                current = 0
            end
            if tonumber(current) < tonumber(ARGV[1]) then
                redis.call('INCR', KEYS[1])
                redis.call('EXPIRE', KEYS[1], ARGV[2])
                return {1, tonumber(current) + 1, tonumber(ARGV[1])}
            else
                local ttl = redis.call('TTL', KEYS[1])
                return {0, tonumber(current), tonumber(ARGV[1]), ttl}
            end";

        var result = await db.ScriptEvaluateAsync(script,
            new RedisKey[] { redisKey },
            new RedisValue[] { limit, window.TotalSeconds });

        var values = (RedisResult[])result!;
        var allowed = (bool)values[0];
        var current = (int)values[1];
        var limitValue = (int)values[2];
        var retryAfter = allowed ? 0 : (int)values[3];

        return new RateLimitResult(
            Allowed: allowed,
            Current: current,
            Limit: limitValue,
            RetryAfter: retryAfter);
    }
}

public record RateLimitResult(bool Allowed, int Current, int Limit, int RetryAfter);

// Custom rate limiting middleware
public class DistributedRateLimitMiddleware
{
    private readonly RequestDelegate _next;
    private readonly RedisRateLimiter _rateLimiter;
    private readonly ILogger<DistributedRateLimitMiddleware> _logger;

    public DistributedRateLimitMiddleware(
        RequestDelegate next,
        RedisRateLimiter rateLimiter,
        ILogger<DistributedRateLimitMiddleware> logger)
    {
        _next = next;
        _rateLimiter = rateLimiter;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var clientId = GetClientIdentifier(context);
        var path = context.Request.Path.Value ?? "";

        // Different limits for different paths
        var (limit, window) = GetLimitForPath(path);

        var result = await _rateLimiter.CheckLimitAsync(
            $"{clientId}:{path}",
            limit,
            window);

        // Add rate limit headers
        AddRateLimitHeaders(context.Response, result);

        if (!result.Allowed)
        {
            _logger.LogWarning(
                "Rate limit exceeded for {ClientId} on {Path}",
                clientId, path);

            context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
            context.Response.Headers.Append("Retry-After", result.RetryAfter.ToString());

            await context.Response.WriteAsJsonAsync(new
            {
                Error = "Rate limit exceeded",
                RetryAfter = result.RetryAfter,
                Limit = result.Limit,
                Window = window.TotalSeconds
            });

            return;
        }

        await _next(context);
    }

    private static (int Limit, TimeSpan Window) GetLimitForPath(string path)
    {
        if (path.StartsWith("/api/login"))
            return (5, TimeSpan.FromMinutes(5));
        if (path.StartsWith("/api/"))
            return (1000, TimeSpan.FromMinutes(1));

        return (100, TimeSpan.FromMinutes(1));
    }

    private static void AddRateLimitHeaders(HttpResponse response, RateLimitResult result)
    {
        response.Headers.Append("X-RateLimit-Limit", result.Limit.ToString());
        response.Headers.Append("X-RateLimit-Remaining", (result.Limit - result.Current).ToString());
    }
}

Pattern 4: User-Based Rate Limiting

Implement rate limiting based on authenticated user identity.

// User-based rate limiter
public class UserBasedRateLimiter
{
    private readonly IRateLimiter _rateLimiter;
    private readonly IUserService _userService;

    public UserBasedRateLimiter(IRateLimiter rateLimiter, IUserService userService)
    {
        _rateLimiter = rateLimiter;
        _userService = userService;
    }

    public async Task<bool> CheckUserLimitAsync(HttpContext context)
    {
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

        if (string.IsNullOrEmpty(userId))
        {
            // Fall back to IP-based limiting for anonymous users
            return await CheckAnonymousLimitAsync(context);
        }

        // Get user's subscription tier
        var user = await _userService.GetUserAsync(userId);
        var (limit, window) = GetLimitForTier(user?.SubscriptionTier);

        var key = $"user:{userId}";
        var result = await _rateLimiter.CheckLimitAsync(key, limit, window);

        AddRateLimitHeaders(context.Response, result);

        return result.Allowed;
    }

    private async Task<bool> CheckAnonymousLimitAsync(HttpContext context)
    {
        var ipAddress = context.Connection.RemoteIpAddress?.ToString() ?? "unknown";
        var key = $"ip:{ipAddress}";

        // Stricter limits for anonymous users
        var result = await _rateLimiter.CheckLimitAsync(key, 30, TimeSpan.FromMinutes(1));

        AddRateLimitHeaders(context.Response, result);

        return result.Allowed;
    }

    private static (int Limit, TimeSpan Window) GetLimitForTier(SubscriptionTier? tier)
    {
        return tier switch
        {
            SubscriptionTier.Enterprise => (10000, TimeSpan.FromMinutes(1)),
            SubscriptionTier.Pro => (1000, TimeSpan.FromMinutes(1)),
            SubscriptionTier.Basic => (100, TimeSpan.FromMinutes(1)),
            _ => (50, TimeSpan.FromMinutes(1)) // Free tier
        };
    }
}

// Middleware integration
public class UserRateLimitMiddleware
{
    private readonly RequestDelegate _next;
    private readonly UserBasedRateLimiter _rateLimiter;

    public UserRateLimitMiddleware(RequestDelegate next, UserBasedRateLimiter rateLimiter)
    {
        _next = next;
        _rateLimiter = rateLimiter;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        if (!await _rateLimiter.CheckUserLimitAsync(context))
        {
            context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
            await context.Response.WriteAsJsonAsync(new
            {
                Error = "Rate limit exceeded",
                UpgradeUrl = "/pricing"
            });
            return;
        }

        await _next(context);
    }
}

// Razor Page with tier-based limiting
public class ApiDashboardModel : PageModel
{
    private readonly IUserRateLimitService _rateLimitService;

    public int CurrentUsage { get; set; }
    public int MonthlyLimit { get; set; }

    public async Task OnGetAsync()
    {
        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)!;

        var usage = await _rateLimitService.GetMonthlyUsageAsync(userId);
        CurrentUsage = usage.Current;
        MonthlyLimit = usage.Limit;
    }
}

Pattern 5: Rate Limiting with Client Identification

Handle various client identification scenarios including proxies and load balancers.

public static class ClientIdentifierHelper
{
    public static string GetClientIdentifier(HttpContext context)
    {
        // 1. Try authenticated user first
        var userId = context.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (!string.IsNullOrEmpty(userId))
        {
            return $"user:{userId}";
        }

        // 2. Try API key
        var apiKey = context.Request.Headers["X-API-Key"].FirstOrDefault();
        if (!string.IsNullOrEmpty(apiKey))
        {
            return $"apikey:{apiKey}";
        }

        // 3. Get IP address (handling proxies)
        var ip = GetClientIpAddress(context);
        return $"ip:{ip}";
    }

    public static string GetClientIpAddress(HttpContext context)
    {
        // Check X-Forwarded-For header (when behind load balancer/proxy)
        var forwardedFor = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
        if (!string.IsNullOrEmpty(forwardedFor))
        {
            // Take the first IP if multiple are present
            var ips = forwardedFor.Split(',', StringSplitOptions.RemoveEmptyEntries);
            if (ips.Length > 0)
            {
                return ips[0].Trim();
            }
        }

        // Check X-Real-IP header
        var realIp = context.Request.Headers["X-Real-IP"].FirstOrDefault();
        if (!string.IsNullOrEmpty(realIp))
        {
            return realIp;
        }

        // Fall back to connection IP
        return context.Connection.RemoteIpAddress?.ToString() ?? "unknown";
    }

    public static bool IsTrustedProxy(HttpContext context, IEnumerable<string> trustedProxies)
    {
        var remoteIp = context.Connection.RemoteIpAddress;
        return remoteIp != null && trustedProxies.Any(proxy =>
        {
            if (IPAddress.TryParse(proxy, out var trustedIp))
            {
                return remoteIp.Equals(trustedIp);
            }
            return false;
        });
    }
}

// Configuration for forwarded headers (Program.cs)
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});

// Use forwarded headers middleware
app.UseForwardedHeaders();

Anti-Patterns

// ❌ BAD: Same limits for all endpoints
options.AddFixedWindowLimiter("default", opt =>
{
    opt.PermitLimit = 100;
    opt.Window = TimeSpan.FromMinutes(1);
});
// Applied to everything - login endpoints need stricter limits!

// ✅ GOOD: Different policies for different endpoints
options.AddFixedWindowLimiter("login", opt =>
{
    opt.PermitLimit = 5; // Strict for authentication
    opt.Window = TimeSpan.FromMinutes(5);
});

options.AddFixedWindowLimiter("api", opt =>
{
    opt.PermitLimit = 1000; // Generous for API
    opt.Window = TimeSpan.FromMinutes(1);
});

// ❌ BAD: No headers indicating rate limit status
// Clients can't track their usage

// ✅ GOOD: Include rate limit headers
context.Response.Headers.Append("X-RateLimit-Limit", limit.ToString());
context.Response.Headers.Append("X-RateLimit-Remaining", remaining.ToString());
context.Response.Headers.Append("X-RateLimit-Reset", resetTime.ToString());

// ❌ BAD: Wrong middleware order
app.UseRateLimiter();
app.UseAuthentication();
// Can't identify users if auth hasn't run yet!

// ✅ GOOD: Rate limiter after authentication
app.UseAuthentication();
app.UseAuthorization();
app.UseRateLimiter();

// ❌ BAD: Not handling rate limit in-memory only
// Won't work across multiple servers
var limiter = new FixedWindowRateLimiter(new FixedWindowRateLimiterOptions
{
    PermitLimit = 100,
    Window = TimeSpan.FromMinutes(1)
});

// ✅ GOOD: Use distributed storage for multi-server
typeof(DistributedCacheRateLimiter)

// ❌ BAD: No fallback when rate limiter fails
public async Task<bool> CheckLimit(string key)
{
    var result = await _redis.CheckLimitAsync(key); // If Redis fails, whole app fails!
    return result.Allowed;
}

// ✅ GOOD: Graceful degradation
public async Task<bool> CheckLimit(string key)
{
    try
    {
        var result = await _redis.CheckLimitAsync(key);
        return result.Allowed;
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Rate limit check failed, allowing request");
        return true; // Fail open
    }
}

// ❌ BAD: Blocking on rate limit check
public IActionResult GetData()
{
    var allowed = CheckLimitAsync().Result; // Blocks thread!
    if (!allowed) return StatusCode(429);
    // ...
}

// ✅ GOOD: Async rate limiting
public async Task<IActionResult> GetDataAsync()
{
    var allowed = await CheckLimitAsync();
    if (!allowed) return StatusCode(429);
    // ...
}

// ❌ BAD: Logging every blocked request at Error level
// Creates log spam during attacks

// ✅ GOOD: Log at appropriate level with sampling
_logger.LogWarning("Rate limit exceeded for {ClientId}", clientId);

// Or use metrics instead
_metrics.RecordRateLimitHit(clientId);

References

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

34.57%
按下载量换算40

Claude

31.08%
按下载量换算36

Cursor

16.42%
按下载量换算19

Gemini CLI

9.07%
按下载量换算10

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills