🌀

DjangoRESTframeworkのスロットリング機能で時間あたりのAPIアクセス制限を設ける

2022/10/31に公開

前提

バックエンド
python 3.7.10
Django 3.2
djangorestframework==3.12.4

フロント
React(今回は関係なし)

課題

SPAの登録処理で、ボタンを連打するとエンドポイントを叩く処理が複数回走ってしまう

対策

・フロント側でボタン押下後に非活性にするとかなんとか
・バックエンド側で、同一ユーザから同じエンドポイントを叩ける回数を毎秒1回にする

本記事は後者の解説。viewをDRFで用意していて、authenticationはさすがにsettingsで設定済という提。

コーディング

before

views.py
class HogeCreateAPIView(generics.CreateAPIView):
    serializer_class = HogeCreateSerializer

after

views.py
from rest_framework.throttling import ScopedRateThrottle
class CustomRateThrottel(ScopedRateThrottle):
    '''
    期間は次のいずれかで設定: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')
    '''
    THROTTLE_RATES = {
        'hoge_create': '1/second', # 2重登録防止目的なので、毎秒1回までのアクセスとする
    }


class HogeCreateAPIView(generics.CreateAPIView):
    serializer_class = HogeCreateSerializer
    throttle_classes = [CustomRateThrottel]
    throttle_scope = 'hoge_create'

viewsets.ModelViewSetでも可。
CustomRateThrottelクラスを用意しなくてもsettingsのREST_FRAMEWORKで設定できる。

実行結果

HogeCreateAPIViewを叩くエンドポイント(store/)を1秒以内に2回たたいた場合
以下のようになる

スロットリングによるアクセス制限に引っかかった場合、DRFは429エラーで返す。

レスポンスデータ

{"detail":"リクエストの処理は絞られました。 Expected available in 1 second."}

Expected available in x secondの時間だけDRFがアクセスを制限している状態となる。
ちなみに制限する時間はデフォルトだと設定した分母に依存する。
minなら 60 - [アクセス制限から経過した時間]
hourなら3600 - [アクセス制限から経過した時間]
:
分母のレパートリーや、アクセス制限時間もDRFのthrottling配下のクラスでカスタマイズできそう。

参考:DRFのScopedRateThrottle
https://www.django-rest-framework.org/api-guide/throttling/

Discussion