Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django rest framework - how can i limit requests to an API endpoint?

Tags:

python

django

I created an API using Django Rest Framework, now i'm working on a rate limiting system, to avoid spam. The built-in throttling system works great, and i managed to add multiple throttles:

REST_FRAMEWORK = {
        # 'DEFAULT_AUTHENTICATION_CLASSES': (
        #     "xapi.authentication_backends.TokenBackend",
        # ),
        'DEFAULT_THROTTLE_CLASSES': [
            'rest_framework.throttling.AnonRateThrottle',
            'rest_framework.throttling.UserRateThrottle'
        ],
        'DEFAULT_THROTTLE_RATES': {
            'anon': '70/minute',
            'user': '70/minute',
            'user_sec': '2/second',
            'user_min': '120/minute',
            'user_hour': '7200/hour',
        },
        
        'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        )
    }

And in my views.py:

class UserSecThrottle(UserRateThrottle):
    scope = 'user_sec'

class UserMinThrottle(UserRateThrottle):
    scope = 'user_min'

class UserHourThrottle(UserRateThrottle):
    scope = 'user_hour'

So if some user performs more than 120 queries in a minute, that user will be blocked for a minute, if the hour limit is breached, the user is blocked for one hour. Is there some way to decide for how much is a user blocked? For example, if i want to block someone for 10 minutes if they perform more than 120 queries in a minute. Any advice is appreciated.

like image 275
JayK23 Avatar asked Feb 12 '21 14:02

JayK23


1 Answers

To create a custom throttle, override BaseThrottle and implement .allow_request(self, request, view). The method should return True if the request should be allowed, and False otherwise.

Optionally you may also override the .wait() method. If implemented, .wait() should return a recommended number of seconds to wait before attempting the next request, or None. The .wait() method will only be called if .allow_request() has previously returned False.

If the .wait() method is implemented and the request is throttled, then a Retry-After header will be included in the response.

import random
class CustomThrottle(throttling.BaseThrottle):
    def allow_request(self, request, view):
         """
         Return `True` if the request should be allowed, `False` otherwise.
         """
         return random.randint(1, 10) != 1

    def wait(self):
        """
        Optionally, return a recommended number of seconds to wait before
        the next request.
        """
        cu_second = 600
        return cu_second

class UserSecThrottle(CustomThrottle,UserRateThrottle):   # or AnonRateThrottle
    scope = 'user_sec'

class ExampleView(APIView):
    throttle_classes = [UserSecThrottle]
    .......

Reference: https://www.django-rest-framework.org/api-guide/throttling/#custom-throttles

like image 64
Pradip Avatar answered Sep 20 '22 23:09

Pradip