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
Другие вопросы по тегам