1. 程式人生 > 其它 >Django Rest Framework使用者訪問頻率限制(轉載)

Django Rest Framework使用者訪問頻率限制(轉載)

一.REST framework的請求生命週期

基於rest-framework的請求處理,與常規的url配置不同,通常一個django的url請求對應一個檢視函式,在使用rest-framework時,我們要基於檢視物件,然後呼叫檢視物件的as_view函式,as_view函式中會呼叫rest_framework/views.py中的dispatch函式,這個函式會根據request請求方法,去呼叫我們在view物件中定義的對應的方法,就像這樣:
from app01 import views as app01_view

urlpatterns = [
    url(r'^limits/'
, api_view.LimitView.as_view()), ]

二. 例項程式碼

1. 程式碼

from rest_framework.views import APIView
from rest_framework import exceptions
from rest_framework.response import Response
from rest_framework.throttling import SimpleRateThrottle



class MySimpleRateThrottle(SimpleRateThrottle):
    scope = "limit
" def get_cache_key(self, request, view): return self.get_ident(request) class LimitView(APIView): authentication_classes = [] permission_classes = [] throttle_classes = [MySimpleRateThrottle, ] # 自定義分流類 def get(self, request, *args, **kwargs): self.dispatch
return Response('控制訪問頻率示例') def throttled(self, request, wait): class MyThrottled(exceptions.Throttled): default_detail = '請求被限制.' extra_detail_singular = 'Expected available in {wait} second.' extra_detail_plural = '還需要再等待{wait}' raise MyThrottled(wait)

2. 執行流程

    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        # 1. 對request進行加工
        # request封裝了
        """
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
        """
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            # 初始化request
            # 確定request版本,使用者認證,許可權控制,使用者訪問頻率限制
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)
        # 6. 二次加工request
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
dispatch
    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        # 2. 確定request版本資訊
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        # 3. 使用者認證
        self.perform_authentication(request)
        # 4. 許可權控制
        self.check_permissions(request)
        # 5. 使用者訪問頻率限制
        self.check_throttles(request)
initial
def check_throttles(self, request):
    """
    Check if request should be throttled.
    Raises an appropriate exception if the request is throttled.
    """
    for throttle in self.get_throttles():
        if not throttle.allow_request(request, self):
            self.throttled(request, throttle.wait())
check_throttles
def get_throttles(self):
    """
    Instantiates and returns the list of throttles that this view uses.
    """
    return [throttle() for throttle in self.throttle_classes]
get_throttles
class APIView(View):

    # The following policies may be set at either globally, or per-view.
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
    metadata_class = api_settings.DEFAULT_METADATA_CLASS
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

    # Allow dependency injection of other settings to make testing easier.
    settings = api_settings

    schema = DefaultSchema()
throttle_classes

3. 執行throttle中allow_request方法

def allow_request(self, request, view):
    """
    Implement the check to see if the request should be throttled.

    On success calls `throttle_success`.
    On failure calls `throttle_failure`.
    """
    if self.rate is None:
        return True

    self.key = self.get_cache_key(request, view)
    if self.key is None:
        return True

    self.history = self.cache.get(self.key, [])
    self.now = self.timer()

    # Drop any requests from the history which have now passed the
    # throttle duration
    while self.history and self.history[-1] <= self.now - self.duration:
        self.history.pop()
    if len(self.history) >= self.num_requests:
        return self.throttle_failure()
    return self.throttle_success()
自定義類繼承SimpleRateThrottle
def get_cache_key(self, request, view):
    """
    Should return a unique cache-key which can be used for throttling.
    Must be overridden.

    May return `None` if the request should not be throttled.
    """
    raise NotImplementedError('.get_cache_key() must be overridden')
get_cache_key

4. 處理報錯異常

def throttled(self, request, wait):
    """
    If request is throttled, determine what kind of exception to raise.
    """
    raise exceptions.Throttled(wait)
throttled
class Throttled(APIException):
    status_code = status.HTTP_429_TOO_MANY_REQUESTS
    default_detail = _('Request was throttled.')
    extra_detail_singular = 'Expected available in {wait} second.'
    extra_detail_plural = 'Expected available in {wait} seconds.'
    default_code = 'throttled'

    def __init__(self, wait=None, detail=None, code=None):
        if detail is None:
            detail = force_text(self.default_detail)
        if wait is not None:
            wait = math.ceil(wait)
            detail = ' '.join((
                detail,
                force_text(ungettext(self.extra_detail_singular.format(wait=wait),
                                     self.extra_detail_plural.format(wait=wait),
                                     wait))))
        self.wait = wait
        super(Throttled, self).__init__(detail, code)
exceptions.Throttled

5. 重寫throttled方法處理異常

def throttled(self, request, wait):

    class MyThrottled(exceptions.Throttled):
        default_detail = '請求被限制.'
        extra_detail_singular = 'Expected available in {wait} second.'
        extra_detail_plural = '還需要再等待{wait}'

    raise MyThrottled(wait)
重寫throttled方法

三. settings.py配置全域性

1. 配置全侷限流速度

REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER': None,
    'UNAUTHENTICATED_TOKEN': None,
    'DEFAULT_AUTHENTICATION_CLASSES': [

    ],
    'DEFAULT_PERMISSION_CLASSES': [],

    'DEFAULT_THROTTLE_RATES': {
        'anon': '5/minute',
        'user': '10/minute',
        'limit': '2/minute'         # 設定每分鐘訪問次數

    }
}

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': 'cache',
    }
}    
settings.py

2. 訪問2次

3. 超過次數,提示報錯