# 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