web-dev-qa-db-ja.com

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) {...}

これはコンパイルされますが、属性のコードはヒットせず、スロットリングは機能しません。ただし、エラーは表示されません。私は何が欠けていますか?

41
RobVious

ASP.NET MVCコントローラーのアクションフィルターとASP.NET Web APIコントローラーのアクションフィルターを混同しているようです。これらは2つの完全に異なるクラスです。

表示されているのは、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コントローラーアクションでこの属性を使用できます。

44
Darin Dimitrov

提案されたソリューションは正確ではありません。少なくとも5つの理由があります。

  1. キャッシュは異なるスレッド間のインターロック制御を提供しないため、複数の要求を同時に処理して、スロットルをスキップする追加の呼び出しを導入できます。
  2. FilterはWeb APIパイプライン内で「ゲームの後半」で処理されているため、リクエストを処理しないと判断する前に多くのリソースが消費されています。 DelegatingHandlerは、Web APIパイプラインの開始時に実行するように設定でき、追加の作業を行う前に要求を切断することができるため、使用する必要があります。
  3. Httpキャッシュ自体は依存関係であり、自己ホスト型オプションのように、新しいランタイムでは使用できない場合があります。この依存関係は避けるのが最善です。
  4. 上記の例のキャッシュは、メモリのプレッシャー、特に優先度が低いために削除される可能性があるため、呼び出し間の生存を保証しません。
  5. それほど悪い問題ではありませんが、応答ステータスを「競合」に設定することは最良の選択肢ではないようです。代わりに「429-too many requests」を使用することをお勧めします。

スロットルを実装する際に解決すべき多くの問題と隠れた障害があります。無料のオープンソースオプションが利用可能です。 https://throttlewebapi.codeplex.com/ を参照することをお勧めします。

48
lenny12345

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()
});

同じ名前のナゲットとしても利用できます。

31
Korayem

アクションフィルターのusingステートメントを再確認してください。 APIコントローラーを使用しているので、System.Web.Http.FiltersのActionFilterAttributeおよびSystem.Web.Mvcの-​​notを参照していることを確認してください。

using System.Web.Http.Filters;
4
Ant P

ショートメッセージ送信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
Bruce

私の2セントは、パラメーターの要求情報に関する「キー」の追加情報を追加するため、同じIPから異なるパラメーター要求が許可されます。

key = Name + clientIP + actionContext.ActionArguments.Values.ToString()

また、「clientIP」に関する私の小さな懸念は、2人の異なるユーザーが同じISPを使用して同じ「clientIP」を持つことは可能ですか?はいの場合、1つのクライアントが誤ってスロットルされる可能性があります。

1
RyanShao