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?
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.
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 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.
The proposed solution is not accurate. There are at least 5 reasons for it.
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.
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:
System.Web.Mvc.ActionFilterAttribute
-> that's what you got from the linkSystem.Web.Http.Filters.ActionFilterAttribute
-> that's what you need to implementIt 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With