RobVious RobVious - 2 months ago 12x
C# Question

How to throttle requests in a Web Api?

I'm trying to implement request throttling via the following:

Best way to implement request throttling in ASP.NET MVC?

I've pulled that code into my solution and decorated an API controller endpoint with the attribute:

[Throttle(Name = "TestThrottle", Message = "You must wait {n} seconds before accessing this url again.", Seconds = 5)]
public HttpResponseMessage DoThis(int id) {...}

This compiles but the attribute's code doesn't get hit, and the throttling doesn't work. I don't get any errors though. What am I missing?


You seem to be confusing action filters for an ASP.NET MVC controller and action filters for an ASP.NET Web API controller. Those are 2 completely different classes:

It appears that what you have shown is a Web API controller action (one that is declared inside a controller deriving from ApiController). So if you want to apply custom filters to it, they must derive from System.Web.Http.Filters.ActionFilterAttribute.

So let's go ahead and adapt the code for Web API:

public class ThrottleAttribute : ActionFilterAttribute
    /// <summary>
    /// A unique name for this Throttle.
    /// </summary>
    /// <remarks>
    /// We'll be inserting a Cache record based on this name and client IP, e.g. "Name-"
    /// </remarks>
    public string Name { get; set; }

    /// <summary>
    /// The number of seconds clients must wait before executing this decorated route again.
    /// </summary>
    public int Seconds { get; set; }

    /// <summary>
    /// A text message that will be sent to the client upon throttling.  You can include the token {n} to
    /// show this.Seconds in the message, e.g. "Wait {n} seconds before trying again".
    /// </summary>
    public string Message { get; set; }

    public override void OnActionExecuting(HttpActionContext actionContext)
        var key = string.Concat(Name, "-", GetClientIp(actionContext.Request));
        var allowExecute = false;

        if (HttpRuntime.Cache[key] == null)
                true, // is this the smallest data we can have?
                null, // no dependencies
                DateTime.Now.AddSeconds(Seconds), // absolute expiration
                null); // no callback

            allowExecute = true;

        if (!allowExecute)
            if (string.IsNullOrEmpty(Message))
                Message = "You may only perform this action every {n} seconds.";

            actionContext.Response = actionContext.Request.CreateResponse(
                Message.Replace("{n}", Seconds.ToString())

where the GetClientIp method comes from this post.

Now you can use this attribute on your Web API controller action.