zoukankan      html  css  js  c++  java
  • 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. 超过次数,提示报错

     
     
     
     
     
     
     
  • 相关阅读:
    android apk瘦身之 图片压缩 tinypng
    java 1.7 新io 实践 NIO2
    Still unable to dial persistent://blog.csdn.net:80 after 3 attempts
    dex2oat 加载多次
    android stadio open recent 在同一窗口打开
    &运算符的应用
    MethodTrace 生成的trace文件为空
    MethodTrace 生成的trace文件为空
    error: unknown host service 的详细解决办法
    error: unknown host service 的详细解决办法
  • 原文地址:https://www.cnblogs.com/supery007/p/8423769.html
Copyright © 2011-2022 走看看