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