Django REST framework之访问频率控制/节流

1. 访问频率控制基本实现

可以根据 ip地址 来对用户访问频率进行限制,所以我们可以自定这样的访问频率控制的类:

还可以通过具有唯一标识意义的用户名或者用户ID等!

throttle.py:

import time

VISIT_RECORD = {}


class VisitThrottle:
    def allow_request(self, request, view):
        """
        设定10s之内只能访问10次
        :param request:
        :param view:
        :return: True or False
        返回值为True表示可以访问;返回值为False或None表示访问频率太高被限制
        """
        # 获取用户的ip地址,当前request(封装)中有的就取当前的request,如果没有就到_request中取
        remote_addr = request._request.META.get('REMOTE_ADDR')
        print(remote_addr)  # 127.0.0.1
        ctime = time.time()
        if remote_addr not in VISIT_RECORD:
            VISIT_RECORD[remote_addr] = [ctime]
            return True
        history = VISIT_RECORD.get(remote_addr)
        while history and history[-1] < ctime - 10:
            history.pop(-1)
        if len(history) < 10:
            history.insert(0, ctime)
            return True
        return False  # 写不写都可以,如果执行到这里说明不可访问的,返回False或None都可以表示不可以访问

    def wait(self, *args, **kwargs):
        pass

views.py:

from rest_framework.views import APIView
from .models import User, UserToken
from django.http import JsonResponse
from utils.md5 import md5
from app.throttle import VisitThrottle


class AuthView(APIView):
    authentication_classes = []
    permission_classes = []
    throttle_classes = [VisitThrottle, ]

    def post(self, request, *args, **kwargs):
        ret = {'code': 1000, 'msg': None}
        try:
            # 需要以form-data的方式提交
            name = request._request.POST.get('name')
            pwd = request._request.POST.get('pwd')
            instance = User.objects.filter(name=name, pwd=pwd).first()  # User object (1),
            print(type(instance))  # <class 'app.models.User'>,加不加all()结果一样
            print(instance)  # User object (1),加不加all()结果一样
            if not instance:
                ret['code'] = 1001
                ret['msg'] = '用户名或密码错误'
            else:
                token = md5(name=name)
                UserToken.objects.update_or_create(user=instance, defaults={'token': token})
                ret['token'] = token
        except Exception as e:
            ret['code'] = 1001
            ret['msg'] = '请求异常'
        return JsonResponse(ret)

10s内只能发送10次请求,超出10次请求,则会返回。请求被限制:
在这里插入图片描述
当请求被拒绝后,系统还可以返回 距离下次可以请求的时间,可以通过wait方法来设置:

throttle.py:

import time
# 可以放到缓存中,默认Django REST framework中就是放在缓存中
VISIT_RECORD = {}


class VisitThrottle:
    def __init__(self):
        self.history = None
        self.ctime = None

    def allow_request(self, request, view):
        """
        设定10s之内只能访问10次
        :param request:
        :param view:
        :return: True or False
        返回值为True表示可以访问;返回值为False或None表示访问频率太高被限制
        """
        # 获取用户的ip地址,当前request(封装)中有的就取当前的request,如果没有就到_request中取
        remote_addr = request._request.META.get('REMOTE_ADDR')
        print(remote_addr)  # 127.0.0.1
        self.ctime = time.time()  # 1593151531.1494734
        print(self.ctime)
        if remote_addr not in VISIT_RECORD:
            VISIT_RECORD[remote_addr] = [self.ctime]
            return True
        self.history = VISIT_RECORD.get(remote_addr)
        while self.history and self.history[-1] < self.ctime - 10:
            self.history.pop(-1)
        if len(self.history) < 10:
            self.history.insert(0, self.ctime)
            return True
        return False  # 写不写都可以,如果执行到这里说明不可访问的,返回False或None都可以表示不可以访问

    def wait(self, *args, **kwargs):
        """
        设置距离下次可以请求的时间
        :param args:
        :param kwargs:
        :return:默认返回None,表示使用默认
        """
        """
        当请求被拒绝,会执行wait方法
        """
        ctime = time.time()
        print(ctime)
        print(self.history[-1])
        """
        return False
        {
          "detail": "Request was throttled. Expected available in 0 seconds."
        }
        """
        return 60 - (ctime - self.history[-1])

在这里插入图片描述

2. 访问频率控制源码流程

请求过来先走dispatch方法:
在这里插入图片描述
执行当前self.initialize_request方法:
在这里插入图片描述
执行self.check_throttles方法:
在这里插入图片描述
执行self.get_throttles方法,遍历访问控制类对象列表:
在这里插入图片描述
通过列表生成式生成访问控制类对象列表:
在这里插入图片描述
默认的访问控制类是从配置文件中获取的,也是全局的访问控制类:
在这里插入图片描述
在这里插入图片描述
如果访问被拒绝,即执行allow_request方法返回False或者None,则执行throttle.wait()方法:
在这里插入图片描述
在这里插入图片描述

3. 访问频率全局配置

同认证和权限一样在配置文件中进行配置:

settings.py:

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': ['app.throttle.VisitThrottle',]
}

同样某些请求可以不使用访问频率控制:

class AuthView(APIView):
    authentication_classes = []
    permission_classes = []
    # 访问频率控制
    throttle_classes = []

    def post(self, request, *args, **kwargs):
        pass
4. 内置访问频率控制类

Django REST framewor中内置了五个访问频率控制类,所有的访问权限控制类应该继承BaseThrottle并重写其中的allow_request(self, request, view)方法:
在这里插入图片描述
在这里插入图片描述
内置的访问控制类SimpleRateThrottle其实帮助我们实现了基于IP的访问权限控制,我们看它内部是怎么实现的:
在这里插入图片描述
在这里插入图片描述
所以之前自定义的类实现的功能完全可以继承SimpleRateThrottle来实现,首先自定义类继承SimpleRateThrottle,并设置scope:

from rest_framework.throttling import SimpleRateThrottle

class VisitThrottle(SimpleRateThrottle):
    # scope被当作key使用的,根据它到配置文件中取值
    scope = 'erics'

在配置文件中添加DEFAULT_THROTTLE_RATES:

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        # 每分钟访问3次
        'erics': '3/m'
    }
}

SimpleRateThrottle类中的逻辑是首先根据scope从配置文件中获取访问频率配置 3/m
在这里插入图片描述
然后执行将获取到的rate也就是scope传入parse_rate函数进行解析:
在这里插入图片描述
在这里插入图片描述
__init__构造函数执行完成之后,执行allow_request方法:
在这里插入图片描述
获取key,把ip当作key:

class VisitThrottle(SimpleRateThrottle):
    # scope被当作key使用的,根据它到配置文件中取值
    scope = 'erics'

    def get_cache_key(self, request, view):
        """获取key"""
        return self.get_ident()

接下来到缓存获取所有记录:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
之前自定义的时间,wait方法也已经做好,我们不需要去写:
在这里插入图片描述
最终我们只需要几行代码就可以完成对匿名用户的访问频率的控制:

from rest_framework.throttling import SimpleRateThrottle

class VisitThrottle(SimpleRateThrottle):
    # scope被当作key使用的,根据它到配置文件中取值
    scope = 'erics'

    def get_cache_key(self, request, view):
        """获取key"""
        return self.get_ident(request)
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': ['app.throttle.VisitThrottle', ],
    'DEFAULT_THROTTLE_RATES': {
        # 每分钟访问5次,/右边只要首字母是m就可以了
        'erics': '5/m'
    }
}

匿名用户每分钟只能访问5次:
在这里插入图片描述
上面是对匿名用户的访问登录控制,也可以对登录用户做访问频率控制。只需要根据用户名来做频率控制条件:

from rest_framework.throttling import SimpleRateThrottle

class UserVisitThrottle(SimpleRateThrottle):
    # scope被当作key使用的,根据它到配置文件中取值
    scope = 'user_erics'

    def get_cache_key(self, request, view):
        """获取key"""
        print(request)  # <rest_framework.request.Request object at 0x7f155c6b86d0>
        # 用户认证成功之后就会有request.user
        print(request.user)  # User object (1)
        print(request.user.name)  # thanlon
        return request.user.name
REST_FRAMEWORK = {
    # 'DEFAULT_THROTTLE_CLASSES': ['app.throttle.VisitThrottle', ],
    # 登录的用户使用根据用户名来做频率限制,匿名用户使用IP来做频率限制。这里全局配置了登录用户的,匿名用户需要可以单独设置通过ip来控制
    'DEFAULT_THROTTLE_CLASSES': ['app.throttle.UserVisitThrottle', ],
    'DEFAULT_THROTTLE_RATES': {
        # 每分钟访问5次,/右边只要首字母是m就可以了
        'erics': '5/m',
        'user_erics': '10/m'
    }
}

登录的用户每分钟只能访问10次:
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章