私は次の方法でリクエスト調整を実装しようとしています:
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) {...}
これはコンパイルされますが、属性のコードはヒットせず、スロットリングは機能しません。ただし、エラーは表示されません。私は何が欠けていますか?
ASP.NET MVCコントローラーのアクションフィルターとASP.NET Web APIコントローラーのアクションフィルターを混同しているようです。これらは2つの完全に異なるクラスです。
System.Web.Mvc.ActionFilterAttribute
->それはあなたがリンクから得たものですSystem.Web.Http.Filters.ActionFilterAttribute
->それはあなたが実装する必要があるものです表示されているのは、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())
);
}
}
}
GetClientIp
メソッドは this post
。
これで、Web APIコントローラーアクションでこの属性を使用できます。
提案されたソリューションは正確ではありません。少なくとも5つの理由があります。
スロットルを実装する際に解決すべき多くの問題と隠れた障害があります。無料のオープンソースオプションが利用可能です。 https://throttlewebapi.codeplex.com/ を参照することをお勧めします。
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()
});
同じ名前のナゲットとしても利用できます。
アクションフィルターのusing
ステートメントを再確認してください。 APIコントローラーを使用しているので、System.Web.Http.Filters
のActionFilterAttributeおよびSystem.Web.Mvc
の-notを参照していることを確認してください。
using System.Web.Http.Filters;
ショートメッセージ送信APIの呼び出し速度を制限するためにThrottleAttribute
を使用していますが、時々機能しないことがわかりました。 APIは、スロットルロジックが機能するまで何度も呼び出される可能性があり、最終的にはSystem.Web.Caching.MemoryCache
の代わりに HttpRuntime.Cache
と問題は解決されたようです。
if (MemoryCache.Default[key] == null)
{
MemoryCache.Default.Set(key, true, DateTime.Now.AddSeconds(Seconds));
allowExecute = true;
}
私の2セントは、パラメーターの要求情報に関する「キー」の追加情報を追加するため、同じIPから異なるパラメーター要求が許可されます。
key = Name + clientIP + actionContext.ActionArguments.Values.ToString()
また、「clientIP」に関する私の小さな懸念は、2人の異なるユーザーが同じISPを使用して同じ「clientIP」を持つことは可能ですか?はいの場合、1つのクライアントが誤ってスロットルされる可能性があります。