2

为了与 slack 交互,服务器需要能够基于一些加密散列验证请求。如果此检查返回 false,则服务器应以 400 响应。将其作为 mixin 执行似乎是明智的:

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 question,我添加了以下内容:

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'
4

2 回答 2

5

首先解决方案:您的第二种方法很好,您只需要实例化JSONResponse类(DRF在get_renderers方法中执行此操作views.APIView):

response.accepted_renderer = JSONRenderer()

背景:

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

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


您添加了一个 answer,您在其中设置上述属性,然后从渲染器返回一个空字典作为响应。如果你想遵循这条路线,一个更简单和更清晰的选择是创建一个子类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.

于 2019-12-02T14:58:49.377 回答
0

创建一个不返回任何内容的渲染器似乎可以让它工作。如果这是“正确”的方式,我会感到惊讶,但它可以完成工作。

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
于 2019-12-02T14:15:37.543 回答