Skip to content

Commit 9e4bc9d

Browse files
authored
Merge pull request #5 from futurum-dev/feature/rate-limiting
Adding 'RateLimiting' example to '.Sample'
2 parents 4ceb9e0 + 30d5c53 commit 9e4bc9d

File tree

6 files changed

+76
-2
lines changed

6 files changed

+76
-2
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ There are examples showing the following:
527527
- [x] File(s) upload with Payload
528528
- [x] File download
529529
- [x] OpenApi version support
530+
- [x] RateLimiting
530531
- [x] Security with a basic JWT example on a specific WebApiEndpoint
531532
- [x] Validation - DataAnnotations and FluentValidation and both combined
532533
- [x] Weather Forecast
@@ -556,12 +557,12 @@ This is used to configure *ApiVersioning* and *ApiExplorer*.
556557

557558
There is an overload of *AddWebApiEndpoints* that takes a generic type of *IWebApiVersionConfigurationService*.
558559
```csharp
559-
builder.Services.AddWebApiEndpoints<CustomWebApiVersionConfigurationService>(
560+
builder.Services.AddWebApiEndpoints<CustomWebApiVersionConfigurationService>();
560561
```
561562

562563
Use this instead
563564
```csharp
564-
builder.Services.AddWebApiEndpoints(
565+
builder.Services.AddWebApiEndpoints();
565566
```
566567

567568
### IWebApiEndpointMetadataStrategy
2.89 KB
Loading
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Futurum.WebApiEndpoint.Micro.Sample.Features;
2+
3+
[WebApiEndpoint("rate-limiting")]
4+
public class RateLimitingWebApiEndpoint : IWebApiEndpoint
5+
{
6+
public void Configure(RouteGroupBuilder groupBuilder, WebApiEndpointVersion webApiEndpointVersion)
7+
{
8+
groupBuilder.RequireRateLimiting(RateLimiting.SlidingWindow.PolicyName);
9+
}
10+
11+
public void Register(IEndpointRouteBuilder builder)
12+
{
13+
builder.MapGet("/", GetHandler);
14+
}
15+
16+
private static Ok<DataCollectionDto<FeatureDto>> GetHandler(HttpContext context) =>
17+
Enumerable.Range(0, 10)
18+
.Select(i => new Feature($"Name - {i}"))
19+
.Select(FeatureMapper.Map)
20+
.ToDataCollectionDto()
21+
.ToOk();
22+
}

sample/Futurum.WebApiEndpoint.Micro.Sample/Futurum.WebApiEndpoint.Micro.Sample.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
1616
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.4" />
1717
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
18+
<PackageReference Include="System.Threading.RateLimiting" Version="7.0.0" />
1819
</ItemGroup>
1920

2021
<ItemGroup>

sample/Futurum.WebApiEndpoint.Micro.Sample/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
builder.AddAuthentication();
1313

14+
builder.Services.AddRateLimiter(RateLimiting.SlidingWindow.Options);
15+
1416
builder.Services
1517
.AddWebApiEndpoints(new WebApiEndpointConfiguration(WebApiEndpointVersions.V1_0)
1618
{
@@ -34,6 +36,8 @@
3436

3537
var app = builder.Build();
3638

39+
app.UseRateLimiter();
40+
3741
app.UseWebApiEndpoints();
3842

3943
app.UseExceptionHandler();
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System.Net;
2+
using System.Threading.RateLimiting;
3+
4+
using Microsoft.AspNetCore.RateLimiting;
5+
6+
namespace Futurum.WebApiEndpoint.Micro.Sample;
7+
8+
public static class RateLimiting
9+
{
10+
public static class SlidingWindow
11+
{
12+
public const string PolicyName = "SlidingWindowPolicy";
13+
14+
public static readonly Action<RateLimiterOptions> Options = options =>
15+
{
16+
options.AddSlidingWindowLimiter(PolicyName,
17+
slidingWindowRateLimiterOptions =>
18+
{
19+
slidingWindowRateLimiterOptions.PermitLimit = 2;
20+
slidingWindowRateLimiterOptions.Window = TimeSpan.FromSeconds(5);
21+
slidingWindowRateLimiterOptions.SegmentsPerWindow = 2;
22+
slidingWindowRateLimiterOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
23+
slidingWindowRateLimiterOptions.QueueLimit = 2;
24+
});
25+
26+
ConfigureOnRejected(options);
27+
};
28+
}
29+
30+
private static void ConfigureOnRejected(RateLimiterOptions options)
31+
{
32+
options.OnRejected = async (context, cancellationToken) =>
33+
{
34+
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.TooManyRequests;
35+
36+
if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
37+
{
38+
await context.HttpContext.Response.WriteAsync($"Too many requests. Please try again after {retryAfter.TotalMinutes} minute(s)...", cancellationToken);
39+
}
40+
else
41+
{
42+
await context.HttpContext.Response.WriteAsync("Too many requests. Please try again later...", cancellationToken);
43+
}
44+
};
45+
}
46+
}

0 commit comments

Comments
 (0)