rest framework of the current limiting components

A custom limiting

Also called limiting frequency component assembly, for controlling the client can request frequency the API, for example, one minute visit 3, if more than three times within one minute to the client to be limiting.

1, custom limiting

Suppose now that an API to access, access can not be more than 3 times in the 30s, how to achieve?

} = {VISIT_RECORD # define global variables, access record for storing 
class VisitThrottle (Object): 

    DEF  the __init__ (Self): 

     # await for calculating the remaining access time self.history
= None DEF allow_request (Self, Request, View): # Get uniquely identifies the user as ip REMOTE_ADDR = request.META.get ( ' REMOTE_ADDR ' ) # Get the current time access the ctime = the time.time () # this is the first user access, which was recorded, and returns True, allow continued access IF REMOTE_ADDR not in VISIT_RECORD: VISIT_RECORD [REMOTE_ADDR] =[ctime,] return True # If this is not the first visit, get all the records History = VISIT_RECORD.get (REMOTE_ADDR) self.history = History # timeframe to determine the beginning of time and the present time whether the difference in the provision of within, for example, in the 60s, if not, # may be removed beginning time of recording the while History and History [-1] <the ctime - 30 : history.pop () # at this point in time are recorded in the list a predetermined time the range, i.e. the number of time determined number of visits IF len (History) <. 3 : history.insert (0, the ctime) return True DEF the wait (Self): # have to wait to access many seconds ctime = time.time() return 60 - (ctime - self.history[-1])

It is configured in the corresponding views:

class BookView(ListAPIView):
    throttle_classes = [VisitThrottle,] #配置限流组件
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer

2, limiting principle

  In the rest framework frame, it is defined as the limiting list of classes, only global configuration or can be partially disposed. Said metering principle is to uniquely identify the client as a key, the time to access a list of values ​​is formed as a dictionary is formed, and then by operating the dictionary:

{
    http://127.0.0.1:8020/ :[11:43:30,11:42:22,11:42:20,11:42:09]
}

  As shown in the above dictionary, is inserted behind the access time to put on the left end, time is added to the current access 11:43 :: 30, then the beginning and 11:42:09 for calculating the difference between the access time and the predetermined time 30s were compared, if not within the 30s, then remove the recording leftmost, empathy compared in turn use a while loop, the last record within the specified time range:

{
  http://127.0.0.1:8020/ :[11:43:30,]
}

Then calculate the number of visits, which is the number of the list, the list obviously if the number is less than 3 can continue to access, otherwise not.

  The above use global variables to be recorded, of course, also be used to store records of the cache, the cache need django API, from django.core.cache import cache, after introducing the API set and get methods may be used to set and retrieve stored in the object cache, only need to be replaced in addition to the operation of global variables:

from django.core.cache Import Cache AS default_cache
 Import Time 

class VisitThrottle (Object): 

    Cache = default_cache 
     
    DEF allow_request (Self, Request, View): 
         ... 
         ...     
        # This is the first time a user visits, which was recorded and returns True, allow continued access 

        IF  not self.cache.get (REMOTE_ADDR): 
            self.cache.set (REMOTE_ADDR, [ctime,]) 
            return True
         # If this is not the first visit, get all the records 

        History = Self. cache.get (REMOTE_ADDR) 
        self.history = History 
        ...
        ...

Limiting component rest framework is based cache to complete.  

  The wait method described above also indicate how long can access this API, suggesting to the client:

{
    "detail": "Request was throttled. Expected available in 56 seconds."
}

Second, the built-in current limit

In the rest framework has some limiting API that can be used:

1、SimpleRateThrottle

class SimpleRateThrottle(BaseThrottle):
    """
    A simple cache implementation, that only requires `.get_cache_key()`
    to be overridden.

    The rate (requests / seconds) is set by a `rate` attribute on the View
    class.  The attribute is a string of the form 'number_of_requests/period'.

    Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')

    Previous request information used for throttling is stored in the cache.
    """
    cache = default_cache
    timer = time.time
    cache_format = 'throttle_%(scope)s_%(ident)s'
    scope = None
    THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES

    def __init__(self):
        if not getattr(self, 'rate', None):
            self.rate = self.get_rate()
        self.num_requests, self.duration = self.parse_rate(self.rate)

    def get_cache_key(self, request, view):
        """
        Should return a unique cache-key which can be used for throttling.
        Must be overridden.

        May return `None` if the request should not be throttled.
        """
        raise NotImplementedError('.get_cache_key() must be overridden')

    def get_rate(self):
        """
        Determine the string representation of the allowed request 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:
            return self.THROTTLE_RATES[self.scope]
        except KeyError:
            msg = "No default throttle rate set for '%s' scope" % self.scope
            raise ImproperlyConfigured(msg)

    def parse_rate(self, rate):
        """
        Given the request rate string, return a two tuple of:
        <allowed number of requests>, <period of time in seconds>
        """
        if rate is None:
            return (None, None)
        num, period = rate.split('/')
        num_requests = int(num)
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        return (num_requests, duration)

    def allow_request(self, request, view):
        """
        Implement the check to see if the request should be throttled.

        On success calls `throttle_success`.
        On failure calls `throttle_failure`.
        """
        if self.rate is None:
            return True

        self.key = self.get_cache_key(request, view)
        if self.key is None:
            return True

        self.history = self.cache.get(self.key, [])
        self.now = self.timer()

        # Drop any requests from the history which have now passed the
        # throttle duration
        while self.history and self.history[-1] <= self.now - self.duration:
            self.history.pop()
        if len(self.history) >= self.num_requests:
            return self.throttle_failure()
        return self.throttle_success()

    def throttle_success(self):
        """
        Inserts the current request's timestamp along with the key
        into the cache.
        """
        self.history.insert(0, self.now)
        self.cache.set(self.key, self.history, self.duration)
        return True

    def throttle_failure(self):
        """
        Called when a request to the API has failed due to throttling.
        """
        return False

    def wait(self):
        """
        Returns the recommended next request time in seconds.
        """
        if self.history:
            remaining_duration = self.duration - (self.now - self.history[-1])
        else:
            remaining_duration = self.duration

        available_requests = self.num_requests - len(self.history) + 1
        if available_requests <= 0:
            return None

        return remaining_duration / float(available_requests)
SimpleRateThrottle

If you need to use this API to implement functionality, you would need to do some configuration:

  • Inheritance SimpleRateThrottle

Their definition of limiting class needs to inherit SimpleRateThrottle:

class VisitThrottle (SimpleRateThrottle): 
    ...
  • Setting scope
class VisitThrottle(SimpleRateThrottle):
    scope = 'book'
    ...

Disposed in a custom class scope parameter, and also need to configure the settings in DEFAULT_THROTTLE_RATES

= REST_FRAMEWORK { 

    " DEFAULT_THROTTLE_RATES " : {
         " Book " : ' 6 / m ' ,   # 6 per minute access
 
    }
  • Rewrite get_cache_key method
class VisitThrottle (SimpleRateThrottle): 
    scope = ' Book ' 

    DEF get_cache_key (Self, Request, View):
         "" " 
        gain access to the label, such as in ip as indicated 
        : param Request: 
        : param View: 
        : return: 
        " "" 
        REMOTE_ADDR = request.META.get ( ' REMOTE_ADDR ' )
         return REMOTE_ADDR

Get access to uniquely identify ip, of course SimpleRateThrottle inherited BaseThrottle, there are ways to obtain ip in BaseThrottle, you only need to call.

class VisitThrottle(SimpleRateThrottle):
    scope = 'book'

    def get_cache_key(self,request,view):
        return self.get_ident(request)
  • Local Configuration

Just add to the list of classes in the flow restrictor corresponding to the corresponding views:

class BookView (ListAPIView): 
    ... 
    throttle_classes = [VisitThrottle,] # Configure throttle assembly 
    ...
  • Global Configuration

Of course, also be arranged in the global settings in which:

REST_FRAMEWORK = {

"DEFAULT_THROTTLE_CLASSES":["app01.utils.throttle.VisitThrottle"],

    "DEFAULT_THROTTLE_RATES": {
        "book": '6/m',

    }

This also corresponds to completion of the function, in addition to the internal API also provides other may be used.

2、AnonRateThrottle

class AnonRateThrottle(SimpleRateThrottle):
    """
    Limits the rate of API calls that may be made by a anonymous users.

    The IP address of the request will be used as the unique cache key.
    """
    scope = 'anon'

    def get_cache_key(self, request, view):
        if request.user.is_authenticated:
            return None  # Only throttle unauthenticated requests.

        return self.cache_format % {
            'scope': self.scope,
            'ident': self.get_ident(request)
        }
AnonRateThrottle

 Limit unauthenticated user. IP address of the incoming request generated by a unique key to be limiting.

3、UserRateThrottle

class UserRateThrottle(SimpleRateThrottle):
    """
    Limits the rate of API calls that may be made by a given user.

    The user id will be used as a unique cache key if the user is
    authenticated.  For anonymous requests, the IP address of the request will
    be used.
    """
    scope = 'user'

    def get_cache_key(self, request, view):
        if request.user.is_authenticated:
            ident = request.user.pk
        else:
            ident = self.get_ident(request)

        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }
UserRateThrottle

  API request by the user is limited to a given request frequency. The user identifier is used to generate a unique key to be limiting. Unauthenticated requests will fall back to using the IP address of the incoming request to generate a unique key for

limit.

4、ScopedRateThrottle

class ScopedRateThrottle(SimpleRateThrottle):
    """
    Limits the rate of API calls by different amounts for various parts of
    the API.  Any view that has the `throttle_scope` property set will be
    throttled.  The unique cache key will be generated by concatenating the
    user id of the request, and the scope of the view being accessed.
    """
    scope_attr = 'throttle_scope'

    def __init__(self):
        # Override the usual SimpleRateThrottle, because we can't determine
        # the rate until called by the view.
        pass

    def allow_request(self, request, view):
        # We can only determine the scope once we're called by the view.
        self.scope = getattr(view, self.scope_attr, None)

        # If a view does not have a `throttle_scope` always allow the request
        if not self.scope:
            return True

        # Determine the allowed request rate as we normally would during
        # the `__init__` call.
        self.rate = self.get_rate()
        self.num_requests, self.duration = self.parse_rate(self.rate)

        # We can now proceed as normal.
        return super(ScopedRateThrottle, self).allow_request(request, view)

    def get_cache_key(self, request, view):
        """
        If `view.throttle_scope` is not set, don't apply this throttle.

        Otherwise generate the unique cache key by concatenating the user id
        with the '.throttle_scope` property of the view.
        """
        if request.user.is_authenticated:
            ident = request.user.pk
        else:
            ident = self.get_ident(request)

        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }
ScopedRateThrottle

  API can be used to restrict access to a specific portion. Only when the view is available that contains  .throttle_scope time properties will apply this limit. The only limit is then formed by the requested stream key "program" connected with a unique user identifier or IP address

Third, source code analysis

Limiting components and assemblies permissions, authentication component and the like, or start from as_view routing method corresponding view function, you can see the final go or dispatch method of APIView.

1、dispatch

   def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        #rest-framework重构request对象
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?
        try:
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            #这里和CBV一样进行方法的分发
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

Here's dispatch method dispatch in APIView method, where the original request to be reconstructed, and by self.initial (request, * args, ** kwargs) added to the current limiting component.

2、initial

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = Version, scheme 

        # Ensure® that incoming Request The IS permitted 
        self.perform_authentication (Request) # authentication 
        self.check_permissions (Request) # permission check 
        self.check_throttles (Request) # restrictor assembly

3、check_throttles

    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):
                self.throttled(request, throttle.wait())
    def get_throttles(self):
        """
        Instantiates and returns the list of throttles that this view uses.
        """
        return [throttle() for throttle in self.throttle_classes]
get_throttles

  You can see a list of current limiting loop is configured in class view, and apparently limiting each class must have allow_request and wait method, False is returned if allow_request explanation has restricted access to the Executive self.throttled (request , throttle.wait ()).

4、throttled

    def throttled(self, request, wait):
        """
        If request is throttled, determine what kind of exception to raise.
        """
        raise exceptions.Throttled(wait)

That is if you have current limit, it will throw an exception to the client limiting tips.

Details Reference: https://q1mi.github.io/Django-REST-framework-documentation/api-guide/throttling/#anonratethrottle

Guess you like

Origin www.cnblogs.com/shenjianping/p/11494092.html