Python? Restframework (frequency component)

BaseThrottle

(1) take out the visitor ip
 (2) judge that the current ip is not in the ACCESS Dictionary, add it in, and return True directly, indicating the first access, and continue to go down in the dictionary
 (3) circularly judge the list of current ip, which has a value, and the last time of subtracting the list from the current time is greater than 60s, pop this kind of data, so that there is only access time within 60s in the list,
(4) judge that when the list is less than 3, it means less than three accesses in one minute, insert the current time to the first position of the list, return True, and pass successfully
 (5) if it is greater than or equal to 3, it means that it has been accessed more than three times in a minute, and False verification failure is returned

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 = []

SimpleRateThrottle

Use the frequency control component in the component

First define the range of limiting frequency in settings.py
REST_FRAMEWORK={
    "DEFAULT_THROTTLE_RATES": {
        "thro_rate": "10/m"
    }
}

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 {wait} second.'
    extra_detail_plural = 'Expected available in {wait} 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 {wait} Seconds later.'
            extra_detail_plural = 'please {wait} Seconds later.'

        raise Myerrors(wait)

Tags: Python less

Posted on Sun, 10 Nov 2019 09:49:11 -0500 by 00tank