Django Rest Framework:从mixin的调度方法返回响应

时间:2019-12-02 13:10:37

标签: django django-rest-framework dispatch django-drf-renderer

为了与松弛交互,服务器需要能够基于某些加密哈希来验证请求。如果此检查返回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” 基于一个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'

为什么它只需要一个HTTP状态代码,却没有其他数据,为什么还需要一个accepted_renderer?解决这个问题的最简单方法是什么?

以下回答建议使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 个答案:

答案 0 :(得分:2)

首先是解决方案:您的第二种方法很好,您只需实例化JSONResponse类(DRF在get_renderers的{​​{1}}方法中做到这一点)

views.APIView

背景

  • Django response.accepted_renderer = JSONRenderer() (继承自WSGIHandler)调用Basehandler来呈现响应
  • DRF response.render()(从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对象的属性-获取渲染器,媒体类型并传递所选渲染器可能需要的任何额外上下文。


You've added an answer,在其中设置上述属性,然后从渲染器返回空字典作为响应。如果您要遵循这条路线,则更简单,更简洁的选择是创建Response的子类,并从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

答案 1 :(得分: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