zoukankan      html  css  js  c++  java
  • REST-Framework: 频率组件

    目录

    一 频率简介

    二 自定义频率类,自定义频率规则

    三 Rest-Framework中内置频率类全局及局部使用

    局部使用:

    四 内置频率类及全局使用

    五 源码分析


    一 频率简介

    为了控制用户对某个url请求的频率,比如,一分钟以内,只能访问三次

    二 自定义频率类,自定义频率规则

    自定义的逻辑

    '''
    
    (1)取出访问者ip
    
    (2)判断当前ip: 不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
    
    (3)循环判断存放当前ip的列表: 有值则使用当前时间减去列表中的最后一个大于60s的时间,把这种数据pop
    
    掉,这样列表中就只有60s以内的访问时间.
    
    (4)判断列表大小,如果列表小于3,说明一分钟以内访问不足三次(表示可以继续访问),把当前时间插入到列表
    
    第一个位置,返回True,顺利通过; 当大于等于3,说明一分钟内访问不少于三次,返回False验证失败
    
    '''

    代码实现:

    class MyThrottles():
    
    VISIT_RECORD = {}
    
    def __init__(self):
    
    self.history=None
    
    def allow_request(self,request, view):
    
    #(1)取出访问者ip
    
    # print(request.META)
    
    ip=request.META.get('REMOTE_ADDR')
    
    import time
    
    ctime=time.time()
    
    # (2)判断当前ip: 不在访问字典里,添加进去并直接返回True,表示第一次访问
    
    if ip not in self.VISIT_RECORD:
    
    self.VISIT_RECORD[ip]=[ctime,]
    
    return True
    
    self.history=self.VISIT_RECORD.get(ip)
    
    # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个大于60s的时间,把这种数据pop掉,这样列表中只有60s以内的访问时间,
    
    while self.history and ctime-self.history[-1]>60:
    
    self.history.pop()
    
    # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
    
    # (5)当大于等于3,说明一分钟内访问不少于三次,返回False验证失败
    
    if len(self.history)<3:
    
    self.history.insert(0,ctime)
    
    return True
    
    else:
    
    return False
    
    def wait(self):
    
    import time
    
    ctime=time.time()
    
    return 60-(ctime-self.history[-1])

     自定义频率类(分析)

    1 限制某个人,某个ip的访问频次
    
    2 自定义频率类及使用
    from rest_framework.throttling import BaseThrottle
    class MyThrottle(BaseThrottle):
        VISIT_RECORD = {}  # 存用户访问信息的大字典
        def __init__(self):
            self.history = None
        def allow_request(self,request,view):
            # 根据ip进行频率限制,每分钟只能访问3次
            # 限制的逻辑
            '''
            #(1)取出访问者ip
            #(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
            #(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
            #(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
            #(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
            '''
            # (1)取出访问者ip
            # print(request.META)
            ip = request.META.get('REMOTE_ADDR')
            import time
            ctime = time.time()
            # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问
            if ip not in self.VISIT_RECORD:
                self.VISIT_RECORD[ip] = [ctime, ]
                return True
            self.history = self.VISIT_RECORD.get(ip)
            # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
            while self.history and ctime - self.history[-1] > 60:
                self.history.pop()
            # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
            # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
            if len(self.history) < 3:
                self.history.insert(0, ctime)
                return True
            else:
                return False
    
        def wait(self):
            # 还剩多长时间能访问
            import time
            ctime = time.time()
            return 60 - (ctime - self.history[-1])
        
    3 使用
        -局部使用
        -全局使用
        
    View Code

    三 Rest-Framework中内置频率类全局及局部使用

    写一个类,继承自SimpleRateThrottle,(下面的例子是根据ip限制用户访问频率的,同时还要求我们会通过用户来限制)
    SimpleRateThrottle只是内置访问频率类中的一个, 还有两个其他的

    注意:  一定要配置: scope = "xxx"     这里的xxx和settings中配置的DEFAULT_THROTTLE_RATES的值要对应起来

    from rest_framework.throttling import SimpleRateThrottle
    
    class VisitThrottle(SimpleRateThrottle):
    
    scope = 'luffy'
    
    def get_cache_key(self, request, view):
    
    return self.get_ident(request)

    不管是全局使用还是局部使用, 使用频率限制的时候都需要在settings中配置中配置频率信息
    在setting里配置:(一分钟访问三次)

    REST_FRAMEWORK = {
    
    'DEFAULT_THROTTLE_RATES':{
    
    'luffy':'3/m'
    
    }
    
    }

    频率的周期可以设置多种类型: 内置的一共四种类型, 基本可以满足我们的使用要求

    • s  :秒
    • m :分
    • h  :时
    • d  :天

    在视图类里使用

    局部使用:

    和之前的权限和认证组件一样, 直接在需要的的CBV上加上已经使用过的频率和认证组件基本是一致的.

    throttle_classes = [MyThrottles,]

    错误信息的中文提示:(可以把中文的错误信息,改为中文的)

    class Course(APIView):
    
    authentication_classes = [TokenAuth, ]
    
    permission_classes = [UserPermission, ]
    
    throttle_classes = [MyThrottles,]
    
    
    
    def get(self, request):
    
    return HttpResponse('get')
    
    
    
    def post(self, request):
    
    return HttpResponse('post')
    
    def throttled(self, request, wait):
    
    from rest_framework.exceptions import Throttled
    
    class MyThrottled(Throttled):
    
    default_detail = '傻逼啊'
    
    extra_detail_singular = '还有 {wait} second.'
    
    extra_detail_plural = '除了 {wait} seconds.'
    
    raise MyThrottled(wait)

    内置频率限制类:

    • BaseThrottle是所有类的基类:方法:def get_ident(self, request)获取标识,其实就是获取ip,自定义的需要继承它
      如果需要通过用户来限制,在这里做设置

    • AnonRateThrottle:未登录用户ip限制,需要配合auth模块用

    • SimpleRateThrottle:重写此方法,可以实现频率,不需要咱们手写上面自定义的逻辑

    • UserRateThrottle:登录用户频率限制,这个得配合auth模块来用

    • ScopedRateThrottle:应用在局部视图上的(忽略)

    四 内置频率类及全局使用

    REST_FRAMEWORK = {
    
    'DEFAULT_THROTTLE_CLASSES':['app01.utils.VisitThrottle',],
    
    'DEFAULT_THROTTLE_RATES':{
    
    'luffy':'3/m'
    
    }
    
    }

     内置频率类的使用

    1 使用
        -局部使用
            throttle_classes = [auth.MyThrottle,]
        -全局使用
            REST_FRAMEWORK = {
            'DEFAULT_THROTTLE_CLASSES':['app01.auth.MyThrottle',],
            }
    2 内置频率类
        BaseThrottle:基类
        AnonRateThrottle:限制匿名用户的访问次数
        SimpleRateThrottle:咱么自定义扩写它
        ScopedRateThrottle:
        UserRateThrottle:限制登录用户访问次数
        
        
    3 扩展内置频率类(重点记住)
        -写一个类,继承SimpleRateThrottle
        class MySimpleThrottle(SimpleRateThrottle):
            scope = 'xxx'
            def get_cache_key(self, request, view):
                #以ip限制
                return self.get_ident(request)
        -setting.py中配置
            REST_FRAMEWORK = {
                'DEFAULT_THROTTLE_RATES' : {
                    'xxx':'10/m'# key跟scope对应,value是一个时间
                }
            }
        -局部使用和全局使用
            
    
            
    4 源码分析
        -继承SimpleRateThrottle---》allow_request(跟咱么写的一样)
        
        
    5 其它内置频率类
        -限制未登录用户的频率(AnonRateThrottle)(根据ip限制)
            -使用:
                -局部使用,全局使用
                -setting.py中配置
                   'DEFAULT_THROTTLE_RATES' : {
                        'anon':'1/m'
                    }
        -限制登录用户访问次数UserRateThrottle(根据用户id限制)
            -使用:
            -局部使用,全局使用
            -setting.py中配置
            'DEFAULT_THROTTLE_RATES' : {
                'user':'1/m'
            }
        -ScopedRateThrottle(有兴趣看一下,没有就不看了)
    View Code

    五 源码分析

    def check_throttles(self, request):
    
    for throttle in self.get_throttles():
    
    if not throttle.allow_request(request, self):
    
    self.throttled(request, throttle.wait())
    
    def throttled(self, request, wait):
    
    #抛异常,可以自定义异常,实现错误信息的中文显示
    
    raise exceptions.Throttled(wait)
    
    class SimpleRateThrottle(BaseThrottle):
    
    # 咱自己写的放在了全局变量,他的在django的缓存中
    
    cache = default_cache
    
    # 获取当前时间,跟咱写的一样
    
    timer = time.time
    
    # 做了一个字符串格式化,
    
    cache_format = 'throttle_%(scope)s_%(ident)s'
    
    scope = None
    
    # 从配置文件中取DEFAULT_THROTTLE_RATES,所以咱配置文件中应该配置,否则报错
    
    THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
    
    
    
    def __init__(self):
    
    if not getattr(self, 'rate', None):
    
    # 从配置文件中找出scope配置的名字对应的值,比如咱写的‘3/m’,他取出来
    
    self.rate = self.get_rate()
    
    # 解析'3/m',解析成 3 m
    
    self.num_requests, self.duration = self.parse_rate(self.rate)
    
    # 这个方法需要重写
    
    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')
    
    
    
    def get_rate(self):
    
    if not getattr(self, 'scope', None):
    
    msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
    
    self.__class__.__name__)
    
    raise ImproperlyConfigured(msg)
    
    
    
    try:
    
    # 获取在setting里配置的字典中的之,self.scope是 咱写的luffy
    
    return self.THROTTLE_RATES[self.scope]
    
    except KeyError:
    
    msg = "No default throttle rate set for '%s' scope" % self.scope
    
    raise ImproperlyConfigured(msg)
    
    # 解析 3/m这种传参
    
    def parse_rate(self, rate):
    
    """
    
    Given the request rate string, return a two tuple of:
    
    <allowed number of requests>, <period of time in seconds>
    
    """
    
    if rate is None:
    
    return (None, None)
    
    num, period = rate.split('/')
    
    num_requests = int(num)
    
    # 只取了第一位,也就是 3/mimmmmmmm也是代表一分钟
    
    duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
    
    return (num_requests, duration)
    
    # 逻辑跟咱自定义的相同
    
    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()
    
    # 成功返回true,并且插入到缓存中
    
    def throttle_success(self):
    
    """
    
    Inserts the current request's timestamp along with the key
    
    into the cache.
    
    """
    
    self.history.insert(0, self.now)
    
    self.cache.set(self.key, self.history, self.duration)
    
    return True
    
    # 失败返回false
    
    def throttle_failure(self):
    
    """
    
    Called when a request to the API has failed due to throttling.
    
    """
    
    return False
    
    
    
    def wait(self):
    
    """
    
    Returns the recommended next request time in seconds.
    
    """
    
    if self.history:
    
    remaining_duration = self.duration - (self.now - self.history[-1])
    
    else:
    
    remaining_duration = self.duration
    
    
    
    available_requests = self.num_requests - len(self.history) + 1
    
    if available_requests <= 0:
    
    return None
    
    
    
    return remaining_duration / float(available_requests)
     
    每天逼着自己写点东西,终有一天会为自己的变化感动的。这是一个潜移默化的过程,每天坚持编编故事,自己不知不觉就会拥有故事人物的特质的。 Explicit is better than implicit.(清楚优于含糊)
  • 相关阅读:
    每日总结
    每日总结
    团队冲刺阶段二(10)
    团队冲刺阶段二(9)
    团队冲刺阶段二(8)
    团队冲刺阶段二(7)
    团队冲刺阶段二(6)
    团队冲刺阶段二(5)
    团队冲刺阶段二(4)
    根据Panda Global了解,中央政治局第十八次集体学习中指出“区块链技术的集成应用在新的技术革新和产业变革中起着重要作用”
  • 原文地址:https://www.cnblogs.com/kylin5201314/p/13967109.html
Copyright © 2011-2022 走看看