Sto cercando di implementare la limitazione delle richieste tramite il seguente:
Il modo migliore per implementare la limitazione delle richieste in ASP.NET MVC?
Ho inserito il codice nella mia soluzione e decorato un endpoint del controller API con l’attributo:
[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) {...}
Questo viene compilato ma il codice dell’attributo non viene colpito e la limitazione non funziona. Non ho alcun errore però. Cosa mi manca?
Sembra che tu stia confondendo i filtri di azione per un controller ASP.NET MVC e filtri di azione per un controller API Web ASP.NET. Quelli sono 2 classi completamente diverse:
System.Web.Mvc.ActionFilterAttribute
-> questo è ciò che hai ottenuto dal link System.Web.Http.Filters.ActionFilterAttribute
-> questo è ciò che è necessario implementare Sembra che ciò che è stato mostrato sia un’azione del controller API Web (una dichiarata all’interno di un controller derivante da ApiController
). Pertanto, se si desidera applicare filtri personalizzati, devono derivare da System.Web.Http.Filters.ActionFilterAttribute
.
Quindi andiamo avanti e adattiamo il codice per l’API Web:
public class ThrottleAttribute : ActionFilterAttribute { /// /// A unique name for this Throttle. /// /// /// We'll be inserting a Cache record based on this name and client IP, eg "Name-192.168.0.1" /// public string Name { get; set; } /// /// The number of seconds clients must wait before executing this decorated route again. /// public int Seconds { get; set; } /// /// 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, eg "Wait {n} seconds before trying again". /// 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()) ); } } }
dove il metodo GetClientIp
proviene da this post
.
Ora è ansible utilizzare questo attributo sull’azione del controller API Web.
La soluzione proposta non è accurata. Ci sono almeno 5 ragioni per questo.
Ci sono molti altri problemi e ostacoli nascosti da risolvere durante l’implementazione della limitazione. Sono disponibili opzioni open source gratuite. Raccomando di consultare https://throttlewebapi.codeplex.com/ , ad esempio.
WebApiThrottle è piuttosto il campione in questo settore.
È super facile da integrare. Basta aggiungere quanto segue ad App_Start\WebApiConfig.cs
:
config.MessageHandlers.Add(new ThrottlingHandler() { // Generic rate limit applied to ALL APIs Policy = new ThrottlePolicy(perSecond: 1, perMinute: 20, perHour: 200) { IpThrottling = true, ClientThrottling = true, EndpointThrottling = true, EndpointRules = new Dictionary { //Fine tune throttling per specific API here { "api/search", new RateLimits { PerSecond = 10, PerMinute = 100, PerHour = 1000 } } } }, Repository = new CacheRepository() });
È disponibile anche come nuget con lo stesso nome.
Controlla le istruzioni using
nel tuo filtro azione. Poiché stai utilizzando un controller API, assicurati di fare riferimento a ActionFilterAttribute in System.Web.Http.Filters
e non a quello in System.Web.Mvc
.
using System.Web.Http.Filters;
Sto usando ThrottleAttribute
per limitare la frequenza di chiamata della mia API di invio di messaggi brevi, ma a volte ho riscontrato che non funzionava. L’API può essere stata chiamata molte volte fino a quando la logica del throttle funziona, finalmente sto usando System.Web.Caching.MemoryCache
invece di HttpRuntime.Cache
e il problema sembra risolto.
if (MemoryCache.Default[key] == null) { MemoryCache.Default.Set(key, true, DateTime.Now.AddSeconds(Seconds)); allowExecute = true; }
I miei 2 centesimi aggiungono alcune informazioni aggiuntive per la ‘chiave’ sulle informazioni sulla richiesta sui parametri, in modo che la richiesta di parametri diversi sia consentita dallo stesso IP.
key = Name + clientIP + actionContext.ActionArguments.Values.ToString()
Inoltre, la mia piccola preoccupazione per il ‘clientIP’, è ansible che due diversi utenti utilizzino lo stesso ISP con lo stesso ‘clientIP’? Se sì, allora un mio cliente verrà strozzato in modo errato.