Search code examples
c#asp.net-core-webapirate-limiting

Using Rate Limiting in ASP.NET Core 7 Web API by IP address


There is currently a NuGet package that manages rate limiting by IP address called AspNetCoreRateLimit. However, .NET 7 introduced its own version of rate limiting and I wanted to use this instead as it is published by MS. I have not been able to find a good example that imitates this third party package by limiting by IP address. The code I put together is as follows:

builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = 429;
    options.AddPolicy("api", httpContext =>
    {
        var IpAddress = httpContext.Connection.RemoteIpAddress.ToString();

        if (IpAddress != null)
        {
            return RateLimitPartition.GetFixedWindowLimiter(httpContext.Connection.RemoteIpAddress.ToString(),
            partition => new FixedWindowRateLimiterOptions
            {
                AutoReplenishment = true,
                PermitLimit = 5,
                Window = TimeSpan.FromMinutes(1)
            });
        }
        else
        {
            return RateLimitPartition.GetNoLimiter("");
        }
    });

});

However, the issue I am getting is a warning "Warning CS8602: Dereference of a possibly null reference." which I assume is because RemoteIpAddress could be null. I am curious if there is a better way to implement this IP rate limiting using this new .NET 7 library. If it matters, I am planning to host this web api in Azure app services (windows) and it is accessed by a SPA also hosted in an app service.


Solution

  • As you mentioned, the warning about the deference of a possibly null reference is not actually an error. It does come from this line:

    httpContext.Connection.RemoteIpAddress.ToString()
    

    Because the RemoteIpAddress property can be null (and therefore your ToString() call would throw).

    However, this is (in my opinion), unrelated to your actual question, which was: " I am curious if there is a better way to implement this IP rate limiting using this new .NET 7 library"

    There is an example of this directly in the Microsoft documentation: https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?view=aspnetcore-7.0#limiter-with-onrejected-retryafter-and-globallimiter

    Specifically, this code can be found on that page:

    limiterOptions.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, IPAddress>(context =>
    {
        IPAddress? remoteIpAddress = context.Connection.RemoteIpAddress;
    
        if (!IPAddress.IsLoopback(remoteIpAddress!))
        {
            return RateLimitPartition.GetTokenBucketLimiter
            (remoteIpAddress!, _ =>
                new TokenBucketRateLimiterOptions
                {
                    TokenLimit = myOptions.TokenLimit2,
                    QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
                    QueueLimit = myOptions.QueueLimit,
                    ReplenishmentPeriod = TimeSpan.FromSeconds(myOptions.ReplenishmentPeriod),
                    TokensPerPeriod = myOptions.TokensPerPeriod,
                    AutoReplenishment = myOptions.AutoReplenishment
                });
        }
    
        return RateLimitPartition.GetNoLimiter(IPAddress.Loopback);
    });
    

    This particular code is setting up a global limiter (sort of besides the point), but if you look at their suggested approach it's truly similar to what you have.

    Instead of converting to a string, they are doing their checks a different way. If you wanted to stick with strings and get away from your warning, you could probably use the following:

    var ipAddressAsString = httpContext.Connection.RemoteIpAddress?.ToString();
    

    Where we have the ?. operator being used. Regardless, I think you're on the right track with your approach.