3月6日学习内容整理:restframework的访问频率控制(节流功能)

一、源码流程

1、URL路由匹配成功后首先执行as_view方法,实际上执行的是API的as_view,其中又用super方法执行V的as_view,再是执行V的view方法,再是执行dispatch方法,注意这里就会执行API中的dispatch方法

2、同样执行API中的initialize_request方法,将原生的request重新封装为Reuqest对象,就行执行initial函数,其中的check_throttles方法就是用来做节流操作的

    def initial(self, request, *args, **kwargs):

        # 认证方法
        self.perform_authentication(request)
        # 权限方法
        self.check_permissions(request)
        # 节流方法
        self.check_throttles(request)

3、在权限方法check_throttles中,会循环throttle_classes列表中存放着的每一个权限类对象,依次执行权限对象的allow_request方法

 

二、节流函数的返回值,也就是allow_request

返回true,代表可以访问

返回false,代表限制访问

 

三、基本使用,代码举例

import time
VISIT_RECORD = {}
# 自定义节流类
class VisitThrottle(object):
    """60s内只能访问3次"""

    def __init__(self):
        self.history = None
    # 具体做节流控制
    def allow_request(self,request,view):
        # 1. 获取用户IP
        remote_addr = request.META.get('REMOTE_ADDR')
        ctime = time.time()
        if remote_addr not in VISIT_RECORD:
            VISIT_RECORD[remote_addr] = [ctime,]
            return True
        history = VISIT_RECORD.get(remote_addr)
        self.history = history
    
        while history and history[-1] < ctime - 60:
            history.pop()
    
        if len(history) < 3:
            history.insert(0,ctime)
            return True
    
        # return True    # 表示可以继续访问
        # return False # 表示访问频率太高,被限制
    # 提示用户需等待多长时间就能访问,返回等待时间
    def wait(self):
        """
        还需要等多少秒才能访问
        :return:
        """
        ctime = time.time()
        return 60 - (ctime - self.history[-1])

class AuthView(APIView):
    """
    用于用户登录认证
    """
    authentication_classes = []
    permission_classes = []
    # 利用这个参数就会使用自定义节流类,而不是去找rest默认的节流类
    throttle_classes = [VisitThrottle,]

    def post(self,request,*args,**kwargs):


        ret = {'code':1000,'msg':None}
        try:
            user = request._request.POST.get('username')
            pwd = request._request.POST.get('password')
            obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
            if not obj:
                ret['code'] = 1001
                ret['msg'] = "用户名或密码错误"
            # 为登录用户创建token
            token = md5(user)
            # 存在就更新,不存在就创建
            models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
            ret['token'] = token
        except Exception as e:
            ret['code'] = 1002
            ret['msg'] = '请求异常'
    
        return JsonResponse(ret)

 

注意:我们在写allow_request方法的时候一般不抛异常,返回false的话rest就会自动抛异常了

获取用户IP的方法(也就是请求的IP):request.META.get('REMOTE_ADDR')

 

四、全局局部视图使用和rest的内置节流类

1、局部使用:仍然是在自定义view类中定义好列表参数throttle_classes,存放自定义节流类

2、全局使用:在配置文件中进行配置

REST_FRAMEWORK = {
# 用做全局节流类的设置,同样是列表,保存节流类的路径,所有的视图都要经过这个节流类
"DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"],
# 下面这两个参数是自定义节流类继承SimpleRateThrottle内置类的时候设置
"DEFAULT_THROTTLE_RATES":{
  # 代表一分钟三次访问限制
    "Luffy":'3/m',
    # 代表一分钟10次访问限制
    "LuffyUser":'10/m',
    }
}

 

 

涉及到的内置类:

# 都在这里面

from rest_framework.throttling import BaseThrottle,SimpleRateThrottle

(1)自定义节流类继承BaseThrottle,就要自己写allow_request和wait方法,无论是全局还是局部

class BaseThrottle(object):
    def allow_request(self, request, view):

        raise NotImplementedError('.allow_request() must be overridden'
    

    # 主要用来获取用户IP
    def get_ident(self, request):
        xff = request.META.get('HTTP_X_FORWARDED_FOR')
        remote_addr = request.META.get('REMOTE_ADDR')
        num_proxies = api_settings.NUM_PROXIES

        if num_proxies is not None:
            if num_proxies == 0 or xff is None:
                return remote_addr
            addrs = xff.split(',')
            client_addr = addrs[-min(num_proxies, len(addrs))]
            return client_addr.strip()

        return ''.join(xff.split()) if xff else remote_addr

    def wait(self):
        return None

 

(2)自定义节流类继承SimpleRateThrottle,自定义节流类就要写成下面的形式:

scope参数跟配置文件中的DEFAULT_THROTTLE_RATES的字典中的key对应,自己写get_cache_key方法

class VisitThrottle(SimpleRateThrottle):
    scope = "Luffy"

    def get_cache_key(self, request, view):
        return self.get_ident(request)


class UserThrottle(SimpleRateThrottle):
    scope = "LuffyUser"

    def get_cache_key(self, request, view):
        return request.user.username

 

posted @ 2018-03-06 19:15  九二零  阅读(128)  评论(0编辑  收藏  举报