1. Distribute display
def dispatch(self, request, *args, **kwargs): try: # Enter initialization self.initial(request, *args, **kwargs)
2. drf initialization method
APIview Next method def initial(self, request, *args, **kwargs): # Authentication self.perform_authentication(request) # Access authority self.check_permissions(request) # > frequency self.check_throttles(request)
3. Frequency module
def check_throttles(self, request): """ Check if request should be throttled. Raises an appropriate exception if the request is throttled. """ # The loop is a list for throttle in self.get_throttles(): # Return the result true or false, false to continue execution if not throttle.allow_request(request, self): # Three parameters of view class, self, request, # throttle.wait(): the number returned by the last wait self.throttled(request, throttle.wait())
3.1,for throttle in self.get_throttles():
def get_throttles(self): """ Instantiates and returns the list of throttles that this view uses. """ # Just like the permission component, the circular return is also a list return [throttle() for throttle in self.throttle_classes]
3.2 if judgment
# When false is returned, the user or order does not have this permission, # If not false is true, continue to execute. If not true is false, do not execute the following code if not throttle.allow_request(request, self):
3.3,throttle.allow_request
# If it is false, it will directly return this error def allow_request(self, request, view): """ Return `True` if the request should be allowed, `False` otherwise. """ raise NotImplementedError('.allow_request() must be overridden')
4,BasePermission
# Inherit the basic authentication permission. If you forget which class you want to define, it's OK to see it here from rest_framework.throttling import BaseThrottle # Custom components def allow_request(self, request, view): raise NotImplementedError('.allow_request() must be overridden') 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 # Last time to return or other def wait(self): return None
5. Define a permission
class MyPermission(BasePermission): # Error returned by foreground message = "You do not have permission, Please contact the administrator first to add permissions" # Get permission def has_permission(self,request, view): # Authentication component, returned request.user print("permission: ", request.user.permission) if request.user.permission > 1: # If greater than is true, in step 3, if not true is equal to false, it will not be executed return True return False
6. Frequency components
class FirstThrottle(BaseThrottle): get_ip = {} def __init__(self): self.history = None self.ip = None self.ctime = time.time() def allow_request(self, request, view): :param request: Data requested by browser :param view: apiview view :return: true or false # 1. Retrieve the IP address of the visitor client_ip = request.META.get("REMOTE_ADDR") self.ip = client_ip # 2. Add the word that does not exist to the dictionary and add the time together if client_ip not in self.get_ip: self.get_ip[client_ip] = [self.ctime, ] return True # Get access time record of current IP self.history = self.get_ip[client_ip] # 3. Start circular judgment. If the last one is longer than 60 seconds, it will be killed directly while self.history and self.ctime - self.history[-1] > 60: self.history.pop() if len(self.history) < 3: self.history.insert(0, self.ctime) return True return False def wait(self): last_time = self.ctime - self.history[-1] - 10 if last_time == 0: self.get_ip[self.ip].clear() return last_time
7. Global usage frequency
# Defined in settings.py file, all components can be put here REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": [ 'app01.myauth.FirstThrottle', # Global access ] }
7. Local use
Class throttle_classes = [FirstThrottle, ]8. Local prohibition
Class throttle_classes = [] SimpleRateThrottleUse the frequency control component in the component
1. Access frequency
class SimpleRateThrottle(BaseThrottle): cache = default_cache # Acquisition time timer = time.time cache_format = 'throttle_%(scope)s_%(ident)s' # This is the dictionary key of the default? Thread? Rates set in setting. It must be defined scope = None THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES # Initialization, def __init__(self): # First of all, judge whether rate is empty. If false is empty, enter self.get \ if not getattr(self, 'rate', None): # Direct output error self.rate = self.get_rate() # If you passed the previous step, continue to 9.2 self.num_requests, self.duration = self.parse_rate(self.rate) # That is to say, the result obtained after 9.2 is # self.num_requests, self.duration = (10,60)
1.1,get_rate
def get_rate(self): # scope must be defined as the value defined in settings, such as thro rate if not getattr(self, 'scope', None): msg = ("You must set either `.scope` or `.rate` for '%s' throttle" % self.__class__.__name__) raise ImproperlyConfigured(msg) try: # Take thro rate out of the configuration file and return 10/m return self.THROTTLE_RATES[self.scope] except KeyError: msg = "No default throttle rate set for '%s' scope" % self.scope raise ImproperlyConfigured(msg)
2. When initialization passes
self.num_requests, self.duration = self.parse_rate(self.rate)
def parse_rate(self, rate): """ Given the request rate string, return a two tuple of: <allowed number of requests>, <period of time in seconds> """ # This is set in setting. If the dictionary of default? Throttle? Rates is set to null, it will directly return none,none if rate is None: return (None, None) # The rate here is the 10/m taken out by get rate and then cut it. num, period = rate.split('/') num_requests = int(num) # If it's m, it's 60 seconds. Then it's a direct value in the dictionary. If it's m, it's 60 seconds duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] # Finally, return to them return (num_requests, duration)
3. Call get_cache_key in class
def get_cache_key(self, request, view): """ # A unique cache key that can be used for throttling should be returned. Should return a unique cache-key which can be used for throttling. # Must be overridden, otherwise calling SimpleRateThrottle will also directly report an error Must be overridden. May return `None` if the request should not be throttled. """ raise NotImplementedError('.get_cache_key() must be overridden')
4. Example
class FirstThrottle(SimpleRateThrottle): # The function self.get rate will be called here, and the returned value is 10/m scope = "thro_rate" # If not redefined, an error will be reported because it has to find the ip address from the cache def get_cache_key(self, request, view): # It's OK to return to null, and there will be a countdown return self.get_ident(request) # "detail": "Request was throttled. Expected available in 56 seconds."
5. Error log in Chinese
5.1 the first three steps of the process
def check_throttles(self, request): """ Check if request should be throttled. Raises an appropriate exception if the request is throttled. """ for throttle in self.get_throttles(): if not throttle.allow_request(request, self): # Enter throttled if not present self.throttled(request, throttle.wait())
5.2. throttled error prompt
def throttled(self, request, wait): """ If request is throttled, determine what kind of exception to raise. """ # Return error message raise exceptions.Throttled(wait)
5.3 rewrite exceptions method
class Throttled(APIException): status_code = status.HTTP_429_TOO_MANY_REQUESTS default_detail = _('Request was throttled.') extra_detail_singular = 'Expected available in second.' extra_detail_plural = 'Expected available in seconds.'
5.4. Example
from app01.SelfThrottle import FirstThrottle from rest_framework import exceptions class Thor(APIView): # Partial use throttle_classes = [FirstThrottle, ] def get(self, request, *args, **kwargs): return HttpResponse("ok") # Note that you need to override the method in the view class, or inherit def throttled(self, request, wait): class Myerrors(exceptions.Throttled): default_detail = "Frequency limit exceeded" extra_detail_singular = 'please Seconds later.' extra_detail_plural = 'please Seconds later.' raise Myerrors(wait)