django rest 框架中的自定义节流响应
Custom throttling response in django rest framework
我正在将 DRF 用于 rest api,所以现在我正在对我的 api 应用限制。为此,我创建了以下油门范围
userRateThrottle
anonRateThrottle
burstRateThrottle
perViewsThrottles(因视图而异)
目前我收到以下回复:
{"detail":"Request was throttled. Expected available in 32.0 seconds."}
我想要这样的回复:
{"message":"request limit exceeded","availableIn":"32.0 seconds","throttleType":"type"}
DRF 文档中没有任何可定制的内容。如何根据要求自定义我的回复?
为此,您可以实施custom exception handler function that returns the custom response in case of a Throttled
例外。
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 在任何 Throttle 类 限制请求的情况下会引发 Throttled
异常。没有信息传递给 Throttled
异常 throttle_class
正在引发该异常。
我知道这是一个旧线程,但添加到 Rahul 的回答中,这里有一种在消息中包含 throttleType 的方法:
您首先需要覆盖 Throttled 异常 class:
创建名为 rest_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)
在这里,您为引发异常的油门实例添加了一个 kwarg(如果提供)。您还可以覆盖详细消息的行为,并使用 wait
值执行您希望的操作。我决定 不 连接详细信息并等待,而是使用原始详细信息。
接下来,您需要创建一个自定义视图集,将节流器传递给节流异常。创建一个名为 rest_viewsets.py
的文件并创建以下内容:
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)
现在您有一个将存储节流实例的自定义异常,以及一个将实例传递给异常的视图集,下一步是实现一个继承该视图集的视图,并且还使用您列出的节流阀 classes 之一。在你的 views.py
中,在预期的视图下(因为你没有提供,我将其称为 MyViewset
):
from .rest_viewsets import ThrottledViewSet
from rest_framework import throttling
class MyViewset(ThrottledViewSet):
throttle_classes = (throttling.userRateThrottle,) # Add more here as you wish
throttled_exception_class = CustomThrottled # This is the default already, but let's be specific anyway
def get_throttled_message(self, request):
"""Add a custom message to the throttled error."""
return "request limit exceeded"
此时,您的应用将像往常一样检查限制,但也会 传递限制实例。我也已将油门消息覆盖为您想要的。我们现在可以利用 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
此时您可以轻松访问油门 class 的任何其他属性,但您只需要 class 名称。
最后但同样重要的是,将您的处理程序添加到 DRF 设置中:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
您可以通过重写视图的 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"
})
我正在将 DRF 用于 rest api,所以现在我正在对我的 api 应用限制。为此,我创建了以下油门范围
userRateThrottle
anonRateThrottle
burstRateThrottle
perViewsThrottles(因视图而异)
目前我收到以下回复:
{"detail":"Request was throttled. Expected available in 32.0 seconds."}
我想要这样的回复:
{"message":"request limit exceeded","availableIn":"32.0 seconds","throttleType":"type"}
DRF 文档中没有任何可定制的内容。如何根据要求自定义我的回复?
为此,您可以实施custom exception handler function that returns the custom response in case of a Throttled
例外。
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 在任何 Throttle 类 限制请求的情况下会引发 Throttled
异常。没有信息传递给 Throttled
异常 throttle_class
正在引发该异常。
我知道这是一个旧线程,但添加到 Rahul 的回答中,这里有一种在消息中包含 throttleType 的方法:
您首先需要覆盖 Throttled 异常 class:
创建名为
rest_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)
在这里,您为引发异常的油门实例添加了一个 kwarg(如果提供)。您还可以覆盖详细消息的行为,并使用
wait
值执行您希望的操作。我决定 不 连接详细信息并等待,而是使用原始详细信息。接下来,您需要创建一个自定义视图集,将节流器传递给节流异常。创建一个名为
rest_viewsets.py
的文件并创建以下内容: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)
现在您有一个将存储节流实例的自定义异常,以及一个将实例传递给异常的视图集,下一步是实现一个继承该视图集的视图,并且还使用您列出的节流阀 classes 之一。在你的
views.py
中,在预期的视图下(因为你没有提供,我将其称为MyViewset
):from .rest_viewsets import ThrottledViewSet from rest_framework import throttling class MyViewset(ThrottledViewSet): throttle_classes = (throttling.userRateThrottle,) # Add more here as you wish throttled_exception_class = CustomThrottled # This is the default already, but let's be specific anyway def get_throttled_message(self, request): """Add a custom message to the throttled error.""" return "request limit exceeded"
此时,您的应用将像往常一样检查限制,但也会 传递限制实例。我也已将油门消息覆盖为您想要的。我们现在可以利用 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
此时您可以轻松访问油门 class 的任何其他属性,但您只需要 class 名称。
最后但同样重要的是,将您的处理程序添加到 DRF 设置中:
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler' }
您可以通过重写视图的 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"
})