J'essaie d'implémenter la limitation de requête via les éléments suivants:
Meilleur moyen d'implémenter la limitation de requête dans ASP.NET MVC?
J'ai intégré ce code dans ma solution et décoré un noeud final de contrôleur d'API avec l'attribut suivant:
[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) {...}
Ceci compile mais le code de l'attribut n'est pas touché et la limitation ne fonctionne pas. Je ne reçois aucune erreur cependant. Qu'est-ce que je rate?
Vous semblez confondre les filtres d'action pour un contrôleur ASP.NET MVC et les filtres d'action pour un contrôleur API Web ASP.NET. Ce sont 2 classes complètement différentes:
System.Web.Mvc.ActionFilterAttribute
-> vous obtenez ce lien.System.Web.Http.Filters.ActionFilterAttribute
-> c'est ce que vous devez implémenter.Il semble que ce que vous avez montré est une action de contrôleur API Web (déclarée dans un contrôleur dérivé de ApiController
). Ainsi, si vous souhaitez y appliquer des filtres personnalisés, ils doivent être dérivés de System.Web.Http.Filters.ActionFilterAttribute
.
Alors allons-y et adaptons le code pour l'API Web:
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())
);
}
}
}
d'où provient la méthode GetClientIp
this post
.
Vous pouvez maintenant utiliser cet attribut sur votre action de contrôleur API Web.
La solution proposée n'est pas exacte. Il y a au moins 5 raisons pour cela.
Il existe de nombreux autres problèmes et obstacles cachés à résoudre lors de la mise en œuvre de la régulation. Il y a des options open source gratuites disponibles. Je recommande de regarder https://throttlewebapi.codeplex.com/ , par exemple.
WebApiThrottle est tout à fait le champion maintenant dans ce domaine.
C'est super facile à intégrer. Ajoutez simplement ce qui suit à 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<string, RateLimits>
{
//Fine tune throttling per specific API here
{ "api/search", new RateLimits { PerSecond = 10, PerMinute = 100, PerHour = 1000 } }
}
},
Repository = new CacheRepository()
});
Il est également disponible sous forme de nuget portant le même nom.
Revérifiez les instructions using
dans votre filtre d'action. Lorsque vous utilisez un contrôleur API, veillez à référencer ActionFilterAttribute dans System.Web.Http.Filters
et not celui dans System.Web.Mvc
.
using System.Web.Http.Filters;
J'utilise ThrottleAttribute
pour limiter le débit d'appel de mon API d'envoi de messages courts, mais cela ne fonctionnait parfois pas. L'API peut avoir été appelé plusieurs fois jusqu'à ce que la logique de régulation fonctionne. Enfin, j'utilise System.Web.Caching.MemoryCache
au lieu de HttpRuntime.Cache
et le problème semble résolu.
if (MemoryCache.Default[key] == null)
{
MemoryCache.Default.Set(key, true, DateTime.Now.AddSeconds(Seconds));
allowExecute = true;
}
Mes 2 cents ajoutent quelques informations supplémentaires pour 'clé' concernant les informations de requête sur les paramètres, de sorte qu'une requête de paramètre différente soit autorisée à partir d'une même adresse IP.
key = Name + clientIP + actionContext.ActionArguments.Values.ToString()
En outre, mon petit souci au sujet du "clientIP", est-il possible que deux utilisateurs différents utilisent le même fournisseur de services Internet a le même "clientIP"? Si oui, alors un client peut être étranglé à tort.