rate limit sample#13
Conversation
BrennanConroy
left a comment
There was a problem hiding this comment.
We probably also want a sample showing custom IRateLimiterPolicy<T> implementation and usage.
Co-authored-by: Brennan <brecon@microsoft.com>
This comment was marked as resolved.
This comment was marked as resolved.
| } | ||
|
|
||
| public Func<OnRejectedContext, CancellationToken, ValueTask>? | ||
| OnRejected { get => _onRejected; } |
There was a problem hiding this comment.
Is it OK to have OnRejected here and also in RateLimiterOptions directly?
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
Right, policy OnRejected will win.
This comment was marked as off-topic.
This comment was marked as off-topic.
| } | ||
|
|
||
| public Func<OnRejectedContext, CancellationToken, ValueTask>? | ||
| OnRejected { get => _onRejected; } |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
Co-authored-by: Kahbazi <akahbazi@gmail.com>
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
| var builder = WebApplication.CreateBuilder(args); | ||
| var app = builder.Build(); | ||
|
|
||
| static string GetTicks() => DateTime.Now.Ticks.ToString().Substring(14); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
I assume you're just refreshing the browser page when you get limited? This would cause the request to be aborted by the client. Like Will said, we might want to change the behavior here. MVC often swallows the OCE from client aborts and just makes sure the request is aborted. https://source.dot.net/#Microsoft.AspNetCore.Mvc.Core/FileResultHelper.cs,47 |
Yes, I didn't see that behavior when testing with JMeter on an Azure app. After getting that exception in the output window for several days, I can't reproduce that now and get the 429 response I set in |
| } | ||
|
|
||
| context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests; | ||
| context?.HttpContext?.RequestServices?.GetService<ILoggerFactory>()? |
There was a problem hiding this comment.
I don't think theses null checks are necessary.
| context?.HttpContext?.RequestServices?.GetService<ILoggerFactory>()? | |
| context.HttpContext.RequestServices.GetService<ILoggerFactory>() |
| return RateLimitPartition.CreateTokenBucketLimiter("Anon", key => | ||
| new TokenBucketRateLimiterOptions(tokenLimit: 5, | ||
| queueProcessingOrder: QueueProcessingOrder.OldestFirst, | ||
| queueLimit: 1, | ||
| replenishmentPeriod: TimeSpan.FromSeconds(5), | ||
| tokensPerPeriod: 1, | ||
| autoReplenishment: true)); |
There was a problem hiding this comment.
How about to use a NoLimiter here? an unauthenticated user could call this endpoint so many times and block all unauthenticated enpoints for everyone.
There was a problem hiding this comment.
That's a good point and something I'll call out. A DOS attack is something to consider on all limiters and with no limiter. I think the simplest samples I just added will have hard coded limits, the last two sample will use configuration.
| var accessToken = httpContext?.Features?.Get<IAuthenticateResultFeature>()? | ||
| .AuthenticateResult?.Properties?.GetTokenValue("access_token")?.ToString() |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
|
dotnet/aspnetcore#43320 should fix the behavior you're seeing w/ |
|
|
||
| app.Run(); | ||
| // </snippet_adm> | ||
| #elif JWT |
There was a problem hiding this comment.
It's better to also include JWT Auth handler, so it is clear in the sample that where does the "access_token" come form.
builder.Services.AddAuthentication().AddJwt(...);
@Kahbazi I made a new sample for this and removed GetTokenValue("access_token") from the other sample. This one is easy to test with dotnet user-jwts
|
@wtgodbe @BrennanConroy I've just started the rate limiting middleware article so it would be helpful to get this sample reviewed and merged. |
wtgodbe
left a comment
There was a problem hiding this comment.
Seems good other than the 2 API change reactions - it'll also be good to add examples of controllers (using the new attributes) once RC1 comes out.
|
|
||
| static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000"); | ||
|
|
||
| app.UseRateLimiter(new RateLimiterOptions() |
There was a problem hiding this comment.
Long term I'd prefer using AddRateLimiter w/ services: https://github.com/dotnet/aspnetcore/blob/4cf483ee7a1cb470b8b2290a174ad34f450a5eab/src/Middleware/RateLimiting/src/RateLimiterServiceCollectionExtensions.cs#L20
| app.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions); | ||
|
|
||
| app.UseRateLimiter(new RateLimiterOptions() | ||
| .AddNoLimiter(policyName: jwtPolicyName) |
There was a problem hiding this comment.
What's the intention here? Adding a no limiter is functionally equivalent to not adding any endpoint limiter for this endpoint - the global limiter will still run. We're removing AddNoLimiter in rc1 (we are also adding an attribute that allows you to disable rate limiting entirely for an endpoint, including the global limiter)
| } | ||
|
|
||
| public Func<OnRejectedContext, CancellationToken, ValueTask>? | ||
| OnRejected { get => _onRejected; } |
There was a problem hiding this comment.
Right, policy OnRejected will win.
| private readonly MyRateLimitOptions _options; | ||
|
|
||
| public SampleRateLimiterPolicy(ILogger<SampleRateLimiterPolicy> logger, | ||
| IOptions<MyRateLimitOptions> options) |
There was a problem hiding this comment.
@Kahbazi I'm now passing in IOptions so I can use config for setting the values.
| //.AddPolicy<string>(completePolicyName, | ||
| // new SampleRateLimiterPolicy(NullLogger<SampleRateLimiterPolicy>.Instance, | ||
| // (IOptions < MyRateLimitOptions >)myConfigSection)) |
There was a problem hiding this comment.
@Kahbazi how do I pass IOptions<MyRateLimitOptions> here?
There was a problem hiding this comment.
@Kahbazi Is there any advantage to showing new SampleRateLimiterPolicy()? Maybe we should just use .AddPolicy<string, SampleRateLimiterPolicy>(helloPolicy) like we do on the next line.
There was a problem hiding this comment.
how do I pass IOptions here?
When you have the MyRateLimitOptions instance, you can create it with Microsoft.Extensions.Options.Options.Create<MyRateLimitOptions>(options).
@Kahbazi Is there any advantage to showing new SampleRateLimiterPolicy()? Maybe we should just use .AddPolicy<string, SampleRateLimiterPolicy>(helloPolicy) like we do on the next line.
Yeah, I agree. Having just .AddPolicy<string, SampleRateLimiterPolicy>(helloPolicy) for sample is enough I think.

Contributes to dotnet/AspNetCore.Docs#26515
All the supporting MVC and Razor Pages (RP) files were merged in #12 to make it easier to review the relevant code.
When dotnet/aspnetcore#43053 makes it to a preview release, sample will be updated.
Sample lives in this repository. Once code is approved, I'll start working on the doc.