Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

[Route("api/dothis/{id}")] [AcceptVerbs("POST")] [Throttle(Name = "TestThrottle", Message = "You must wait {n} seconds before accessing this url again.", Seconds = 5)] [Authorize] 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?

like image 212
RobVious Avatar asked Dec 28 '13 17:12

RobVious


People also ask

How do I throttle API requests?

One way to implement API throttling in distributed systems is to use sticky sessions. In this method, all requests from a user are always serviced by a particular server. However, this solution is not well-balanced or fault tolerant. The second solution to API throttling in distributed systems are locks.

How do I limit a Web API request?

Sometimes some of the clients consume the API frequently without any limit. But if you want to limit consuming the API for a particular client within a certain time, then you can achieve it by Rate Limiting. Rate Limiting is the process of controlling the number of requests for a resource within a specific time window.

What is Web API throttling?

What is API Throttling? API throttling allows you to control the way an API is used. Throttling allows you to set permissions as to whether certain API calls are valid or not. Throttles indicate a temporary state, and are used to control the data that clients can access through an API.


2 Answers

The proposed solution is not accurate. There are at least 5 reasons for it.

  1. The cache does not provide interlocking control between different threads, therefore multiple requests can be process at the same time introducing extra calls skipping through the throttle.
  2. The Filter is being processed 'too late in the game' within web API pipeline, so lots of resources are being spent before you decide that request should not be processed. The DelegatingHandler should be used because it can be set to run at the beginning of the Web API pipeline and cutting off the request prior doing any additional work.
  3. The Http cache itself is dependency that might not be available with new runtimes, like self-hosted options. It is best to avoid this dependency.
  4. Cache in the above example does not guarantee its survival between the calls as it might be removed due to memory pressure, especially being low priority.
  5. Although it is not too bad issue, setting response status to 'conflict' does not seem to be the best option. It is better to use '429-too many requests' instead.

There are many more issues and hidden obstacles to solve while implementing the throttling. There are free open source options available. I recommend to look at https://throttlewebapi.codeplex.com/, for example.

like image 185
lenny12345 Avatar answered Sep 23 '22 20:09

lenny12345


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:

  • For ASP.NET MVC: System.Web.Mvc.ActionFilterAttribute -> that's what you got from the link
  • For ASP.NET Web API: System.Web.Http.Filters.ActionFilterAttribute -> that's what you need to implement

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-192.168.0.1"     /// </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)         {             HttpRuntime.Cache.Add(key,                 true, // is this the smallest data we can have?                 null, // no dependencies                 DateTime.Now.AddSeconds(Seconds), // absolute expiration                 Cache.NoSlidingExpiration,                 CacheItemPriority.Low,                 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(                 HttpStatusCode.Conflict,                  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.

like image 26
Darin Dimitrov Avatar answered Sep 26 '22 20:09

Darin Dimitrov