# myThrottle.py import time class VisitThrottle(): ''' {'ip1':[时间1 ,时间2], 'ip2':[时间1, ], } ''' visit_dic = {} def __init__(self): self.history = None self.time = time.time def allow_request(self, request, view): # 取出访问者ip ip = request.META.get('REMOTE_ADDR') now_time = self.time() # 判断当前ip不在访问字典里,添加进去,而且直接返回True,表示第一次访问 if ip not in self.visit_dic: self.visit_dic[ip] = [now_time, ] return True # 用户不是第一次访问,直接到这里执行 # 取出用户对应的列表 self.history = self.visit_dic[ip] # 循环判断当前ip的列表,有值,而且列表的最后一个时间加上60s仍是小于当前时间,把这种数据pop掉,这样列表中只有60s之内的访问时间 while self.history and self.history[-1] + 60 < now_time: self.history.pop() # 判断,当列表小于3,说明一分钟之内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利经过 if len(self.history)<3: self.visit_dic[ip].insert(0,now_time) return True # 当大于等于3,说明一分钟内访问超过三次,返回False验证失败 return False # 当访问被限制后,距离下次能够访问还剩的时间 def wait(self): now_time = self.time() return 60 - (now_time - self.history[0])
#views.py from django.shortcuts import render, HttpResponse from rest_framework.views import APIView from app01.myThrottle import VisitThrottle from rest_framework.exceptions import Throttled # Create your views here. class Book(APIView): # 局部使用 throttle_classes = [VisitThrottle, ] def get(self, request): return HttpResponse('ok') # 访问频率限制超出,错误信息(中文) def throttled(self, request, wait): # 自定义异常,并继承Throttled异常 class MyThrottled(Throttled): default_detail = '距离下次访问' extra_detail_singular = '还有 {wait} second.' extra_detail_plural = '还有 {wait} seconds.' # 抛出异常 raise MyThrottled(wait) # 全局使用 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ['app01.myThrottle.VisitThrottle', ] } # 全局使用下局部禁用 class Book(APIView): throttle_classes = []
# settings.py REST_FRAMEWORK = { # 全局使用 'DEFAULT_THROTTLE_CLASSES':['app01.myThrottle.VisitThrottle',], 'DEFAULT_THROTTLE_RATES': { # time: 3次/每分钟 'time': '3/m' } }
# myThrottle.py from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): # 全局配置访问频率 scope = 'time' # 局部配置访问频率 # rate = '3/m' def get_cache_key(self, request, view): # 对什么的访问频率限制就返回什么 # get_ident():SimpleRateThrottle的父类的方法,得到访问的IP return self.get_ident(request)#① # ① # get_ident() def get_ident(self, request): ..... xff = request.META.get('HTTP_X_FORWARDED_FOR') # request.META.get('REMOTE_ADDR') # 从META里得到访问的IP地址 remote_addr = request.META.get('REMOTE_ADDR') num_proxies = api_settings.NUM_PROXIES ..... return ''.join(xff.split()) if xff else remote_addr
#views.py from django.shortcuts import render, HttpResponse from rest_framework.views import APIView from app01.myThrottle import VisitThrottle from rest_framework.exceptions import Throttled # Create your views here. class Book(APIView): # 局部使用 throttle_classes = [VisitThrottle, ] # 局部禁用 # throttle_classes = [] def get(self, request): return HttpResponse('ok') # 访问频率限制超出,错误信息(中文) def throttled(self, request, wait): # 自定义异常,并继承Throttled异常 class MyThrottled(Throttled): default_detail = '距离下次访问' extra_detail_singular = '还有 {wait} second.' extra_detail_plural = '还有 {wait} seconds.' # 抛出异常 raise MyThrottled(wait)
# 第一步 # APIView类 def dispatch(self, request, *args, **kwargs): ........ # 重点是这个,这是认证、频率以及权限相关的 self.initial(request, *args, **kwargs) ........
# 第二步 # APIView类 def initial(self, request, *args, **kwargs): ........ self.perform_authentication(request) self.check_permissions(request) # 频率 self.check_throttles(request)
# 第三步 # APIView类 def check_throttles(self, request): for throttle in self.get_throttles(): # throttle.allow_request的返回值是bool类型 if not throttle.allow_request(request, self): # 若是返回False,触发self.throttled()的执行,抛出异常 # throttle.wait():返回值是数值型,倒计时时间 self.throttled(request, throttle.wait()) # self.get_throttles()的来历 def get_throttles(self): return [throttle() for throttle in self.throttle_classes] # self.throttled(request, throttle.wait()) def throttled(self, request, wait): raise exceptions.Throttled(wait)
# 第一步 # 从自定义的频率控制看出allow_request()是控制频率的主要代码 class SimpleRateThrottle(BaseThrottle): ...... timer = time.time ...... def allow_request(self, request, view): # 1.self.rate if self.rate is None: return True # 2.get_cache_key 得到限制的对象 self.key = self.get_cache_key(request, view) if self.key is None: return True # self.cache.get(self.key, []) # 这个就至关于自定义频率控制中的大字典,是从缓存中得到 # 若是self.cache中有对应self.key的列表,就赋值给self.history # 没有则建立并给默认值[]空列表 self.history = self.cache.get(self.key, []) # timer = time.time # 加上()执行,得到当前的时间戳 self.now = self.timer() # 3.self.num_requests和self.duration # 循环判断,当前ip的列表有值,而且列表的当前时间减去self.duration小于最后一个时间,把这种数据pop掉,这样列表中只有self.duration之内的访问时间 while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() # 4.self.throttle_failure()和self.throttle_success() # 判断当列表大于self.num_requests,说明一分钟之内访问大于或等于self.num_requests次,执行self.throttle_failure()方法返回False验证失败 if len(self.history) >= self.num_requests: return self.throttle_failure() # 当列表小于self.num_requests时,执行self.throttle_success()方法返回True return self.throttle_success()
# 1. # self.rate class SimpleRateThrottle(BaseThrottle): ....... # 全局属性 THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES ....... def __init__(self): # 初始化时判断继承SimpleRateThrottle类的里面有没有rate,没有就执行self.get_rate()去找子类里scope if not getattr(self, 'rate', None): # self.rate = scope所表明的值 self.rate = self.get_rate() self.num_requests, self.duration = self.parse_rate(self.rate) # self.get_rate def get_rate(self): # 若是没有scope就会报错 if not getattr(self, 'scope', None): msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %self.__class__.__name__) raise ImproperlyConfigured(msg) # 若是有的话就去self的THROTTLE_RATES里找,也就是去setting里的DEFAULT_THROTTLE_RATES中找scope值对应的 try: return self.THROTTLE_RATES[self.scope] except KeyError: msg = "No default throttle rate set for '%s' scope" % self.scope raise ImproperlyConfigured(msg)
# 2. # self.get_cache_key class SimpleRateThrottle(BaseThrottle): # 若是继承SimpleRateThrottle类的里面不写,就会抛出异常,因此必须写 def get_cache_key(self, request, view): raise NotImplementedError('.get_cache_key() must be overridden')
# 3. # self.num_requests和self.duration class SimpleRateThrottle(BaseThrottle): ....... # 全局属性 THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES ....... def __init__(self): if not getattr(self, 'rate', None): # self.rate = scope所表明的值,好比‘3/m’ self.rate = self.get_rate() # 把self.rate看成参数给self.parse_rate(),而且把返回值解压分给了self.num_requests和self.duration self.num_requests, self.duration = self.parse_rate(self.rate) # self.parse_rate def parse_rate(self, rate): if rate is None: return (None, None) # 假设rate = ‘3/m’ # num = '3',period = 'm' num, period = rate.split('/') # num_requests = 3 num_requests = int(num) # duration = 60 duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] # return(3,60) 表示3次/60秒 return (num_requests, duration)
#4 # self.throttle_failure()和self.throttle_success() class SimpleRateThrottle(BaseThrottle): def throttle_success(self): # 把当前访问时间插入self.history的第一个 self.history.insert(0, self.now) # 而且存入缓存中 self.cache.set(self.key, self.history, self.duration) return True def throttle_failure(self): # 直接返回False return False
根据请求头 content-type 选择对应的解析器对请求体内容进行处理。python
有application/json,x-www-form-urlencoded,form-data等格式django
from rest_framework.parsers import FileUploadParser, MultiPartParser, JSONParser, FormParser # 局部所有使用 # 视图里 parser_classes = [FileUploadParser, MultiPartParser, JSONParser, FormParser] # 局部单个使用,只使用指定的解释器解释 # 视图里 parser_classes = [FileUploadParser,] # 全局使用 # setting.py REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES':[ 'rest_framework.parsers.JSONParser' 'rest_framework.parsers.FormParser' 'rest_framework.parsers.MultiPartParser' ] }
# 在调用request.data时,才进行解析,由此入手 # request @property def data(self): if not _hasattr(self, '_full_data'): self._load_data_and_files() return self._full_data
# request # self._load_data_and_files() def _load_data_and_files(self): if not _hasattr(self, '_data'): # self._parse() 解析以后 把解析的分到data和files里 self._data, self._files = self._parse() .......
# request # self._parse() def _parse(self): # media_type = 用户请求头里content_type的值 media_type = self.content_type #self里就有content_type,传入此函数 parser = self.negotiator.select_parser(self, self.parsers) # self.content_type def content_type(self): meta = self._request.META return meta.get('CONTENT_TYPE', meta.get('HTTP_CONTENT_TYPE', '')) # self.parsers # self.parsers Request对象实例化时传进来的参数self.get_parsers() def initialize_request(self, request, *args, **kwargs): parser_context = self.get_parser_context(request) return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context ) # self.get_parsers() def get_parsers(self): # 先从类自己找,找不到去父类找即APIVIew 中的 return [parser() for parser in self.parser_classes]
# self.negotiator.select_parser(self, self.parsers) def select_parser(self, request, parsers): # parsers:parser_classes,也就是解析器列表 # 循环解析器列表 for parser in parsers: # parser.media_type:解析器所能解析的类型 # request.content_type:请求中的解析方式 # 若是一致返回解析器,不然返回None if media_type_matches(parser.media_type, request.content_type): return parser return None