.NET CoreでAPIリクエスト回数を制限する方法

.NET CoreでAPIリクエスト回数を制限する方法

AspNetCoreRateLimitのような既存のライブラリは以前ご紹介しましたが、今日は取り上げません。背後にある原理についてお話しします。

最終更新 2022/07/08 6:54
黑哥聊dotNet
読了目安 3 分
カテゴリ
ASP.NET Core
タグ
.NET C# ASP.NET Core

AspNetCoreRateLimitのようなライブラリ(いわゆる「車輪」)については以前紹介しましたので、本日は割愛します。その背後にある原理についてお話ししましょう。ぜひご指摘をお待ちしております!

私たちがよく目にするAPIリクエストインターフェースサイトの例:

例えば、海外主要都市の7日間の天気予報を取得するインターフェースを例に取ると、非VIPユーザーは2000回しか利用できず、VIPユーザーは1日あたり最大10000回までリクエストできます。このインターフェースをリクエストするには、アカウントを登録してappidとシークレットキーを取得する必要があります。

この要件に基づき、天気予報を取得するレート制限付きインターフェースを設計してみましょう。

ステップ1

ログインアカウントが存在するか検証し、存在しない場合はエラーをスローします。

[HttpPost("GetWeather")]
public IActionResult ApiLimit(WeatherInfor weatherInfor)
{
  if (!_userService.IsValid(weatherInfor.Appid, weatherInfor.Appsecret))
  {
    throw new Exception("アカウントまたはパスワードが間違っています");
  }
}

ステップ2

アカウントがVIPユーザーかどうかを判定します。VIPユーザーの場合、総呼び出し回数の制限はなく、1日あたりの制限回数のみが適用されます。このような1日限りの制限は、翌日にリセットされるデータなので、キャッシュを使用して処理するのが合理的です。キャッシュは毎日23時59分59秒にすべてのリクエストが有効かどうかをチェックするように設定します。これがキャッシュの絶対有効期限です。

具体的なビジネスロジックは次のとおりです。ユーザーのappidは一意なので、それをキーとし、呼び出し回数を値とします。キャッシュが存在しない場合は新規追加し、存在する場合は呼び出し回数を取得します。2000回を超えている場合は「呼び出し回数が上限に達しました」と返し、超えていない場合はキャッシュから呼び出し回数を取得し、+1します。

キャッシュクラス

public class MemoryCacheHelper
{

    public static MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());

    /// <summary>
    /// キャッシュアイテムの存在確認
    /// </summary>
    /// <param name="key">キャッシュキー</param>
    /// <returns></returns>
    public static bool Exists(string key)
    {
        if (key == null)
        {
            return false;
        }
        return _cache.TryGetValue(key, out _);
    }

    /// <summary>
    /// キャッシュの取得
    /// </summary>
    /// <param name="key">キャッシュキー</param>
    /// <returns></returns>
    public static object Get(string key)
    {
        if (key == null)
        {
            throw new ArgumentNullException(nameof(key));
        }
        if (!Exists(key))
            throw new ArgumentNullException(nameof(key));


        return _cache.Get(key);
    }

    /// <summary>
    /// キャッシュの追加
    /// </summary>
    /// <param name="key">キャッシュキー</param>
    /// <param name="value">キャッシュ値</param>
    /// <param name="expiresSliding">スライド有効期限(有効期限内に操作がある場合、現在時刻から有効期限を延長)</param>
    /// <param name="expiressAbsoulte">絶対有効期限</param>
    /// <returns></returns>
    public static bool AddMemoryCache(string key, object value)
    {
        if (key == null)
        {
            throw new ArgumentNullException(nameof(key));
        }
        if (value == null)
        {
            throw new ArgumentNullException(nameof(value));
        }
        DateTime time = Convert.ToDateTime(DateTime.Now.AddDays(1).ToString("D").ToString()).AddSeconds(-1);
        _cache.Set(key, value, time);
        return Exists(key);
    }
}

ビジネスロジック

public bool IsLimit(string appId)
{
    if (MemoryCacheHelper.Exists(appId))
    {
        int value = int.Parse(MemoryCacheHelper.Get(appId).ToString());
        if (value > 2000)
            return false;
        else
            value = value + 1;

        MemoryCacheHelper.AddMemoryCache(appId, value);
    }
    else
    {
        MemoryCacheHelper.AddMemoryCache(appId, 1);
    }
    return true;
}

最後に

天気予報インターフェースを呼び出し、データを返します。

[HttpPost("GetWeather")]
public IActionResult ApiLimit(WeatherInfor weatherInfor)
{
    if (!_userService.IsValid(weatherInfor.Appid, weatherInfor.Appsecret))
    {
        throw new Exception("アカウントまたはパスワードが間違っています");
    }
    bool isLimit = false;
    if (_userService.IsVIP(weatherInfor.Appid))
    {
        isLimit= _sqlServices.IsLimit(weatherInfor.Appid);
    }
    else
    {
        isLimit = _memoryCacheServices.IsLimit(weatherInfor.Appid);
    }
    if (isLimit)
    {
        // 天気予報インターフェースを呼び出し、データを返す
    }
    else
    {
        throw new Exception("呼び出し回数が上限に達しました");
    }
    return Ok("");
}

最後に、もし私の記事をお気に召しましたら、ぜひフォローといいねをお願いいたします。.NETエコシステムがますます良くなることを願っています!

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2022/06/22

ASP.NET Core WebAPI でローカリゼーションを実装する(単一リソースファイル)

Microsoft のデフォルトは、1 つのクラスに複数のリソースファイルを対応させる方法であり、使用がやや面倒です。本記事では、単一リソースファイルの使用方法を紹介します。つまり、プロジェクト全体のすべてのクラスが 1 セットの多言語リソースファイルに対応します。

続きを読む