Created
December 18, 2023 07:59
-
-
Save 0x49D1/69a9d097c34b0f5384fc64d76c68be71 to your computer and use it in GitHub Desktop.
Simple attribute used to rate limit requests based on IP address and path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Sample usage in controller action | |
[HttpPost] | |
[Route("in/batch-update")] | |
[RateLimit] | |
public virtual async Task<IActionResult> BatchUpdate(SomeModel model) | |
{...} | |
*/ | |
/// <summary> | |
/// Attribute used to rate limit requests based on IP address and path. | |
/// </summary> | |
public class RateLimitAttribute : TypeFilterAttribute | |
{ | |
public RateLimitAttribute(int limit = 100, int seconds = 30) : base(typeof(RateLimitFilter)) | |
{ | |
Arguments = new object[] { limit, seconds }; | |
} | |
private class RateLimitFilter : IActionFilter | |
{ | |
private static Dictionary<string, Tuple<DateTime, int>> _requests = new Dictionary<string, Tuple<DateTime, int>>(); | |
private readonly int _limit; | |
private readonly int _seconds; | |
private readonly ILogger _logger; | |
public RateLimitFilter(ILogger<RateLimitFilter> logger, int limit, int seconds) | |
{ | |
_logger = logger; | |
_limit = limit; | |
_seconds = seconds; | |
} | |
public void OnActionExecuting(ActionExecutingContext context) | |
{ | |
var ip = context.HttpContext.Connection.RemoteIpAddress.ToString(); | |
var path = context.HttpContext.Request.Path; | |
var key = $"{ip}-{path}"; | |
if (_requests.ContainsKey(key)) | |
{ | |
var value = _requests[key]; | |
if (value.Item1.AddSeconds(_seconds) > DateTime.Now) | |
{ | |
if (value.Item2 > _limit) | |
{ | |
_logger.LogWarning("Rate limit exceeded for {Key}", key); | |
context.Result = new StatusCodeResult(429); // Too many requests | |
return; | |
} | |
else | |
{ | |
_requests[key] = Tuple.Create(value.Item1, value.Item2 + 1); | |
} | |
} | |
else | |
{ | |
_requests.Remove(key); | |
} | |
} | |
if (!_requests.ContainsKey(key)) | |
{ | |
_requests.Add(key, Tuple.Create(DateTime.Now, 1)); | |
} | |
} | |
public void OnActionExecuted(ActionExecutedContext context) | |
{ | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment