I'm using AspNetCoreRateLimit library with Asp.Net Core 2.2 web api. I've taken IpRateLimiting into use with it's default settings in Startup.cs
as seen AspNetCoreRateLimit wiki.
I have API endpoint with query parameters, and it is used with http GET queries as following (see parameters startDate
and stopDate
):
GET "https://host/api/endpoint/path?startDate=2020-04-04&stopDate=2020-04-04"
I want to limit only unique requests (with unique parameter combinations) to 5 requests per hour. So, for example, the following scenario should be possible in 1 hour:
5 times: GET "https://host/api/endpoint/path?startDate=2020-04-04&stopDate=2020-04-04"
5 times: GET "https://host/api/endpoint/path?startDate=2020-04-05&stopDate=2020-04-05"
The problem is that I can send only total 5 requests per hour regardless of parameters.
Following is my IpRateLimiting Settings from appsettings.json.
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIPHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"GeneralRules": [
{
"Endpoint": "*:/api/endpoint/path",
"Period": "1h",
"Limit": 5
}
]
}
Please note, that I don't want to change the endpoint route as proposed in this good answer by @Yongqing Yu, because there are a bunch of API clients out there using my API and I do not want to introduce any breaking changes.
You can change the route
of the corresponding action and turn the parameter directly into a part of the path like 'https://host/api/endpoint/path/2020-04-04/2020-04-04'
, so that the Endpoint
in GeneralRules
can satisfy your condition by *
.
You can refer to this.
Here is my demo:
[Route("api/[controller]")]
[ApiController]
public class DefaultController : ControllerBase
{
[HttpGet("Test/{startDate}/{stopDate}")]
public string Test(string startDate, string stopDate)
{
return "Ok";
}
}
appsettings.json:
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIPHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"GeneralRules": [
{
"Endpoint": "*:/api/default/Test/*",
"Period": "1h",
"Limit": 5
}
]
}
Here is the test result:
I found a solution and thus answering to myself. In my case I am not able to change the controller method route as proposed in another answer.
As mentioned here it is possible to implement own path extraction logic. I wrote custom IpRateLimitMiddleware and overrided ResolveIdentity-method as follows:
public class CustomIpRateLimitMiddleware : IpRateLimitMiddleware
{
private readonly ILogger<CustomIpRateLimitMiddleware> _logger;
private readonly IRateLimitConfiguration _config;
public CustomIpRateLimitMiddleware(RequestDelegate next,
IOptions<IpRateLimitOptions> options,
IRateLimitCounterStore counterStore,
IIpPolicyStore policyStore,
IRateLimitConfiguration config,
ILogger<CustomIpRateLimitMiddleware> logger)
: base(next, options, counterStore, policyStore, config, logger)
{
_config = config;
_logger = logger;
}
public override ClientRequestIdentity ResolveIdentity(HttpContext httpContext)
{
var identity = base.ResolveIdentity(httpContext);
if (httpContext.Request.Query == null && !httpContext.Request.Query.Any())
{
return identity;
}
StringBuilder path = new StringBuilder(httpContext.Request.Path.ToString().ToLowerInvariant());
foreach (var parameter in httpContext.Request.Query)
{
path.Append("/" + parameter.Value);
}
identity.Path = path.ToString();
return identity;
}
}
And this is initialized in Startup.cs as following:
ConfigureServices
-method:
services.AddOptions();
services.AddMemoryCache();
services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));
services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
Configure
-method:
app.UseMiddleware<CustomIpRateLimitMiddleware>();
The code snippet above amends query for the middleware so it looks like those query parameters are part of the path.
So instead of this:
/api/endpoint/path?startDate=2020-04-04&stopDate=2020-04-04"
AspNetCoreRateLimit is getting path as following format:
/api/endpoint/path/2020-04-04/2020-04-04
..and now my rate limiting configuration can be this:
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIPHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"GeneralRules": [
{
"Endpoint": "*:/api/path/*",
"Period": "1h",
"Limit": 5
}
]
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With