如何在Web Api中限制请求?

Rob*_*ous 41 .net c# throttling asp.net-web-api

我正在尝试通过以下方式实现请求限制:

在ASP.NET MVC中实现请求限制的最佳方法?

我已将该代码提取到我的解决方案中,并使用以下属性修饰了API控制器端点:

[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) {...}
Run Code Online (Sandbox Code Playgroud)

这会编译,但属性的代码不会被命中,并且限制不起作用.我没有收到任何错误.我错过了什么?

len*_*345 48

建议的解决方案不准确.至少有5个理由.

  1. 缓存不提供不同线程之间的互锁控制,因此可以同时处理多个请求,从而引入跳过限制的额外调用.
  2. 过滤器正在Web API管道中"在游戏中太晚"处理,因此在您决定不应该处理请求之前,需要花费大量资源.应该使用DelegatingHandler,因为它可以设置为在Web API管道的开头运行,并在执行任何其他工作之前切断请求.
  3. Http缓存本身是新的运行时可能无法使用的依赖项,例如自托管选项.最好避免这种依赖.
  4. 上述示例中的缓存不保证其在调用之间的存活,因为它可能由于内存压力而被移除,尤其是低优先级.
  5. 虽然问题不是太糟糕,但将响应状态设置为"冲突"似乎并不是最佳选择.最好使用'429-too many requests'来代替.

在实施限制时,还有许多问题和隐藏的障碍需要解决.有免费的开源选项.我建议您查看https://throttlewebapi.codeplex.com/.

  • +1不重新发明轮子.我目前正在评估https://www.nuget.org/packages/WebApiThrottle/,看起来很有前景. (7认同)

Dar*_*rov 44

您似乎混淆了ASP.NET MVC控制器的动作过滤器和ASP.NET Web API控制器的动作过滤器.这是两个完全不同的类:

看来,您所显示的是Web API控制器操作(在控制器内部声明的操作ApiController).因此,如果您要对其应用自定义过滤器,则必须从中派生System.Web.Http.Filters.ActionFilterAttribute.

那么让我们继续并调整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())
            );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

其中GetClientIp方法来自this post.

现在,您可以在Web API控制器操作上使用此属性.


Kor*_*yem 32

WebApiThrottle现在已成为该领域的冠军.

它非常容易集成.只需将以下内容添加到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()
});
Run Code Online (Sandbox Code Playgroud)

它也可以作为一个同名的nuget使用.


Ant*_*t P 6

仔细检查using操作过滤器中的语句。当您使用 API 控制器时,请确保您引用的是 ActionFilterAttribute ,System.Web.Http.Filters不是System.Web.Mvc.

using System.Web.Http.Filters;
Run Code Online (Sandbox Code Playgroud)

  • 啊,是的,这样就可以了。虽然这引入了很多错误,因为一切都依赖于 `ActionExecutingContext`,我相信现在需要是 `HttpActionContext` - 现在就完成它。谢谢你! (2认同)