Well, I found that the problem was that the middleware throttler was handling any request to a web page and the MVC throttler was also handling any request to a web page. Each throttler was reading and writing to the same cache, but when they read the value from the cache, one was attempting to cast the object to a WebApiThrottle object and the other was attempting to cast the object to an MvcThrottle object. That was no good.
So I fixed the issue by giving the middleware throttling its own carefully-constructed policy. By default, the policy doesn't throttle anything. Then I add a rule to specifically apply throttling to the security token endpoint.
That way the security token endpoint is still rate limited, but the web page throttling is handled entirely by the MVC throttling attributes. The middleware throttler will ignore anything that isn't overridden in its rules list because the default rate limits are all 0, meaning no throttling. The exception goes away and all is well.
Here's what the middleware throttling policy class looks like:
public class MiddlewareThrottlingPolicy : ThrottlePolicy
{
/// <summary>
/// Creates the middleware throttling policy
/// </summary>
public MiddlewareThrottlingPolicy()
{
//Everything is unthrottled by default. We don't have to do anything to achieve that
//We're throttling by endpoint
EndpointThrottling = true;
//Add the endpoints that get different throttling
RateLimits policySettings = new RateLimits
{
PerSecond = 30,
PerMinute = 100,
PerHour = 200,
PerDay = 500,
PerWeek = 0
};
EndpointRules = new Dictionary<string, RateLimits>
{
{ "/api/authentication/token", policySettings }
};
}
}
In the real application, I load the rate limits from configuration settings, but I simplified it here.
Here's how I apply the middleware throttling policy when starting up the application:
app.Use(typeof(ThrottlingMiddleware),
new MiddlewareThrottlingPolicy(),
new PolicyCacheRepository(),
new CacheRepository(),
null,
null);