Django Rest Framework:Return 来自 mixin 的 dispatch 方法的响应

Django Rest Framework: Return response from mixin's dispatch method

为了与 slack 交互,服务器需要能够基于一些加密散列来验证请求。如果此检查 returns 为假,服务器应以 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)

这给出了错误 "accepted_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'

为什么它需要一个 accepted_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'

创建一个渲染器,returns 似乎没有什么可以让它工作。如果这是 'correct' 方式,我会感到惊讶,但它完成了工作。

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

一开始的解决方法:你的第二种方法没问题,只需要实例化JSONResponseclass即可(DRF在views.APIViewget_renderers方法中实现):

response.accepted_renderer = JSONRenderer()

背景:

  • Django WSGIHandler(继承自Basehandler)调用response.render()渲染响应
  • DRF Response(继承自 SimpleTemplateResponse)对象有一个 render 方法,通过 rendered_content 属性(它调用render 具有传递数据、媒体类型和上下文的渲染器方法)
  • 在最初的content-negotiation阶段,根据DEFAULT_RENDERER_CLASSES/APIView.renderer_classes设置和client传递的Aceeptheader来设置renderer;所选渲染器在 HttpRequest 对象中设置为 accepted_renderer,媒体类型设置为 request.accepted_media_type 属性
  • 如果渲染器需要任何额外的上下文,Response 对象也需要 renderer_context 属性;例如,views.APIView 将当前视图、请求和参数设置为 renderer_context dict

现在应该清楚为什么您需要 Response 对象的属性 -- 获取渲染器、媒体类型并传递所选渲染器可能需要的任何额外上下文。


,您在其中设置上述属性,然后从渲染器 returning 一个空的字典作为响应。如果你想遵循这条路线,一个更简单和更清晰的选择是从 render 方法创建一个 Response 的 subclass 和 return 一个空字典,例如:

class EmptyResponse(rest_framework.response.Response):

     def render(self):
         # You can have your own rendered content here
         self.content = b''
         return self

现在只需要 returning 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)

现在,除非您要添加一些自定义内容,否则不需要延迟呈现;你可以直接 return HttpResponse object:

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)

如果需要,您可以在初始化 HttpResponse 时传递 content(以字节为单位)。但如果由于某种原因,需要惰性渲染,则需要使用Response.render.