Python django rest框架中的自定义调节响应

Python django rest框架中的自定义调节响应,python,django,rest,django-rest-framework,throttling,Python,Django,Rest,Django Rest Framework,Throttling,我将DRF用于RESTAPI,所以现在我将节流应用于我的API。为此,我创建了以下节流作用域 用户速率节流阀 无油门 爆破节流阀 透视窗(视情况而定) 目前我得到以下回复: {“详细信息”:“请求已被限制。预期在32.0秒后可用。”} 我想要这样的回应: {“message”:“超出请求限制”,“availableIn”:“32.0秒”,“throttleType”:“type”} DRF文档中没有用于定制的内容。如何根据需求定制响应?要做到这一点,您可以实现一个,在异常情况下返回自定义响应 f

我将DRF用于RESTAPI,所以现在我将节流应用于我的API。为此,我创建了以下节流作用域

  • 用户速率节流阀

  • 无油门

  • 爆破节流阀

  • 透视窗(视情况而定)

  • 目前我得到以下回复:

    {“详细信息”:“请求已被限制。预期在32.0秒后可用。”}

    我想要这样的回应:

    {“message”:“超出请求限制”,“availableIn”:“32.0秒”,“throttleType”:“type”}


    DRF文档中没有用于定制的内容。如何根据需求定制响应?

    要做到这一点,您可以实现一个,在异常情况下返回自定义响应

    from rest_framework.views import exception_handler
    from rest_framework.exceptions import Throttled
    
    def custom_exception_handler(exc, context):
        # Call REST framework's default exception handler first,
        # to get the standard error response.
        response = exception_handler(exc, context)
    
        if isinstance(exc, Throttled): # check that a Throttled exception is raised
            custom_response_data = { # prepare custom response data
                'message': 'request limit exceeded',
                'availableIn': '%d seconds'%exc.wait
            }
            response.data = custom_response_data # set the custom response data on response object
    
      return response
    
    然后,需要将此自定义异常处理程序添加到DRF设置中

    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
    }
    

    我认为在不更改某些DRF代码的情况下了解
    throttleType
    会有点困难,因为DRF会在任何节流类限制请求的情况下引发
    Throttled
    异常。没有向
    节流的
    异常传递任何有关
    节流类引发该异常的信息。

    我知道这是一个旧线程,但除了Rahul的答案之外,还有一种方法可以在消息中包含节流类型:

    您首先需要覆盖限制的异常类:

  • 创建一个名为
    rest\u exceptions.py
    的文件,并创建以下内容:

    import math
    import inspect
    from django.utils.encoding import force_text
    from django.utils.translation import ungettext
    from rest_framework import exceptions, throttling
    
    class CustomThrottled(exceptions.Throttled):
    
        def __init__(self, wait=None, detail=None, throttle_instance=None):
            if throttle_instance is None:
                self.throttle_instance = None
            else:
                self.throttle_instance = throttle_instance
    
            if detail is not None:
                self.detail = force_text(detail)
            else:
                self.detail = force_text(self.default_detail)
    
            if wait is None:
                self.wait = None
            else:
                self.wait = math.ceil(wait)
    
    from rest_framework import viewsets
    from .rest_exceptions import CustomThrottled
    
    class ThrottledViewSet(viewsets.ViewSet):
        """
        Adds customizability to the throtted method for better clarity.
        """
    
        throttled_exception_class = CustomThrottled
    
        def throttled(self, request, wait, throttle_instance=None):
            """
            If request is throttled, determine what kind of exception to raise.
            """
            raise self.get_throttled_exception_class()(wait, detail=self.get_throttled_message(request),
                                                       throttle_instance=throttle_instance)
    
        def get_throttled_message(self, request):
            """
            Add a custom throttled exception message to pass to the user.
            Note that this does not account for the wait message, which will be added at the
            end of this message.
            """
            return None
    
        def get_throttled_exception_class(self):
            """
            Return the throttled exception class to use.
            """
            return self.throttled_exception_class
    
        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(), throttle_instance=throttle)
    
    在这里,您为引发异常(如果提供)的油门实例添加了一个kwarg。您还可以覆盖详细信息的行为,并使用
    wait
    值执行所需操作。我决定不串联细节并等待,而是使用原始细节消息

  • 接下来,您将要创建一个自定义视图集,该视图集将节流器传递给节流异常。创建名为
    rest\u viewsets.py的文件
    ,并创建以下内容:

    import math
    import inspect
    from django.utils.encoding import force_text
    from django.utils.translation import ungettext
    from rest_framework import exceptions, throttling
    
    class CustomThrottled(exceptions.Throttled):
    
        def __init__(self, wait=None, detail=None, throttle_instance=None):
            if throttle_instance is None:
                self.throttle_instance = None
            else:
                self.throttle_instance = throttle_instance
    
            if detail is not None:
                self.detail = force_text(detail)
            else:
                self.detail = force_text(self.default_detail)
    
            if wait is None:
                self.wait = None
            else:
                self.wait = math.ceil(wait)
    
    from rest_framework import viewsets
    from .rest_exceptions import CustomThrottled
    
    class ThrottledViewSet(viewsets.ViewSet):
        """
        Adds customizability to the throtted method for better clarity.
        """
    
        throttled_exception_class = CustomThrottled
    
        def throttled(self, request, wait, throttle_instance=None):
            """
            If request is throttled, determine what kind of exception to raise.
            """
            raise self.get_throttled_exception_class()(wait, detail=self.get_throttled_message(request),
                                                       throttle_instance=throttle_instance)
    
        def get_throttled_message(self, request):
            """
            Add a custom throttled exception message to pass to the user.
            Note that this does not account for the wait message, which will be added at the
            end of this message.
            """
            return None
    
        def get_throttled_exception_class(self):
            """
            Return the throttled exception class to use.
            """
            return self.throttled_exception_class
    
        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(), throttle_instance=throttle)
    
  • 现在,您有了一个存储throttle实例的自定义异常和一个将实例传递给异常的视图集,接下来的步骤是实现一个继承此视图集的视图,并使用您列出的throttle类之一。在您的
    views.py
    中,在预期视图下(因为您没有提供该视图,所以我将其称为
    MyViewset
    ):

  • 此时,您的应用程序将像往常一样检查油门,但也会传递油门实例。我还根据您的要求覆盖了油门消息。我们现在可以利用Rahul提供的解决方案,只需做一些修改。创建自定义异常处理程序:

    from rest_framework.views import exception_handler
    from .rest_exceptions import CustomThrottled
    
    def custom_exception_handler(exc, context):
        # Call REST framework's default exception handler first,
        # to get the standard error response.
        response = exception_handler(exc, context)
    
        if isinstance(exc, CustomThrottled): # check that a CustomThrottled exception is raised
            custom_response_data = { # prepare custom response data
                'message': exc.detail,
                'availableIn': '%d seconds'%exc.wait,
                'throttleType': type(exc.throttle_instance).__name__
            }
            response.data = custom_response_data # set the custom response data on response object
    
      return response
    
    此时,您可以轻松访问throttle类的任何其他属性,但您只需要类名

  • 最后,将处理程序添加到DRF设置中:

    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
    }
    

  • 您可以通过覆盖视图的
    throttled
    方法来更改throttled响应的消息。例如:

    from rest_framework.exceptions import Throttled
    
    class SomeView(APIView):
        def throttled(self, request, wait):
            raise Throttled(detail={
                  "message":"request limit exceeded",
                  "availableIn":f"{wait} seconds",
                  "throttleType":"type"
            })
    

    哇!非常优雅的解决方案