Django Rest Framework: возврат ответа от метода отправки миксина
Чтобы взаимодействовать с резервом, сервер должен иметь возможность проверять запросы на основе некоторого криптографического хеширования. Если эта проверка вернет false, сервер должен ответить 400. Кажется разумным сделать это как миксин:
class SlackValidationMixin:
def dispatch(self, request, *args, **kwargs):
if validate_slack_request(request):
return super().dispatch(request, *args, **kwargs)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
Это дает ошибку "accept_renderer not set on Response". На основании вопроса SO я добавил следующее:
class SlackValidationMixin:
def dispatch(self, request, *args, **kwargs):
if validate_slack_request(request):
return super().dispatch(request, *args, **kwargs)
else:
response = Response(status=status.HTTP_400_BAD_REQUEST)
response.accepted_renderer = JSONRenderer
response.accepted_media_type = "application/json"
response.renderer_context = {}
return response
Но это дает ошибку: AttributeError: 'NoneType' object has no attribute 'get_indent'
Зачем ему accept_renderer, учитывая, что он отвечает только кодом состояния HTTP, без дополнительных данных? Как это проще всего обойти?
Следующее предложение в ответе, чтобы объект EmptyResponse унаследовал от Response:
Traceback (most recent call last):
File "path/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "path/lib/python3.8/site-packages/django/utils/deprecation.py", line 96, in __call__
response = self.process_response(request, response)
File "path/lib/python3.8/site-packages/django/middleware/common.py", line 106, in process_response
if response.status_code == 404:
AttributeError: 'dict' object has no attribute 'status_code'
2 ответа
Сначала решение: ваш второй подход в порядке, вам нужно только создать экземпляр JSONResponse
класс (DRF делает это в get_renderers
метод views.APIView
):
response.accepted_renderer = JSONRenderer()
Задний план:
- Джанго
WSGIHandler
(унаследовано отBasehandler
) звонкиresponse.render()
дать ответ - DRF
Response
(унаследовано отSimpleTemplateResponse
) объект имеетrender
метод, который получает визуализированный контент черезrendered_content
свойство (которое вызываетrender
метод рендерера с переданными данными, типом носителя и контекстом) - На начальном этапе согласования содержимого средство визуализации настраивается в соответствии с
DEFAULT_RENDERER_CLASSES
/APIView.renderer_classes
установка иAceept
заголовок передан клиентом; выбранный рендерер устанавливается вHttpRequest
объект какaccepted_renderer
и тип носителя какrequest.accepted_media_type
атрибуты - Если рендереру нужен дополнительный контекст,
Response
объект также нуждается вrenderer_context
атрибут; например,views.APIView
устанавливает текущее представление, запрос и аргументы какrenderer_context
диктовать
Теперь должно быть понятно, зачем нужны атрибуты с Response
объект - чтобы получить средство визуализации, тип мультимедиа и передать любой дополнительный контекст, который может потребоваться выбранному средству визуализации.
Вы добавили ответ, в котором вы устанавливаете вышеупомянутые атрибуты, а затем средство визуализации возвращает пустой dict в качестве ответа. Если вы хотите следовать по этому маршруту, гораздо более простым и понятным вариантом было бы создание подклассаResponse
и верните пустой диктант из render
метод, например:
class EmptyResponse(rest_framework.response.Response):
def render(self):
# You can have your own rendered content here
self.content = b''
return self
Теперь только возвращаем EmptyResponse
объект будет делать, нет необходимости добавлять атрибуты, связанные с рендерером:
class SlackValidationMixin:
def dispatch(self, request, *args, **kwargs):
if validate_slack_request(request):
return super().dispatch(request, *args, **kwargs)
else:
return EmptyResponse(status=status.HTTP_400_BAD_REQUEST)
Теперь, если вы не добавляете какой-либо пользовательский контент, отложенный рендеринг не нужен; вы можете напрямую вернутьсяHttpResponse
объект:
from django.http import HttpResponse
class SlackValidationMixin:
def dispatch(self, request, *args, **kwargs):
if validate_slack_request(request):
return super().dispatch(request, *args, **kwargs)
else:
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
А при желании можно пройти content
(в байтах) при инициализации HttpResponse
. Но если по какой-то причине вам нужен ленивый рендеринг, вам нужно использоватьResponse.render
.
Похоже, что создание рендерера, который ничего не возвращает, заставляет это работать. Я был бы удивлен, если бы это был "правильный" способ, но он выполняет свою работу.
class NoneRenderer(BaseRenderer):
def render(self, *args, **kwargs):
return {}
class SlackValidationMixin:
def dispatch(self, request, *args, **kwargs):
if validate_slack_request(request):
return super().dispatch(request, *args, **kwargs)
else:
response = Response(status=status.HTTP_400_BAD_REQUEST)
response.accepted_renderer = NoneRenderer
response.accepted_media_type = "*/*"
response.renderer_context = {}
return response