【问题标题】:Editing response content in Django middleware在 Django 中间件中编辑响应内容
【发布时间】:2017-05-22 12:10:15
【问题描述】:

我有 Django 1.10 项目和以下用户定义的中间件

class RequestLogMiddleWare(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response.data['detail'] = 'I have been edited'
        return response

和 REST 端点视图:

def r_mobile_call_log(request):
    return Response({'success': True, 
                     'detail': 'Before having been edited'}, 
                      status=status.HTTP_200_OK)

所以我希望客户端的最终响应是:

{'success': 'True', 'detail': 'I have been edited'}

但是,我看到的是:

{'success': 'True', 'detail': 'Before having been edited'}

我在中间件的call方法中放了一个断点,以确保函数真的被执行了,就OK了。 response.data["details"] 只是不会改变它的价值。有谁知道这是什么原因?

【问题讨论】:

    标签: django django-rest-framework django-middleware


    【解决方案1】:

    Response 已经在中间件阶段渲染,所以你不能只改变response.data,你需要重新渲染它或者直接改变渲染的内容。

    class RequestLogMiddleWare(object):
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            response = self.get_response(request)
            if isinstance(response, Response):
                response.data['detail'] = 'I have been edited'
                # you need to change private attribute `_is_render` 
                # to call render second time
                response._is_rendered = False 
                response.render()
            return response
    

    第二种方法是直接更改内容,但在这种情况下,内置 REST Framework 浏览器 API 将不起作用,因为模板无法正确呈现。

    import json
    
    class RequestLogMiddleWare(object):
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            response = self.get_response(request)
            if isinstance(response, Response):
                response.data['detail'] = 'I have been edited'
                response.content = json.dumps(response.data)
            return response
    

    source code for render method

    【讨论】:

    • ValueError at ________ Circular reference detected
    【解决方案2】:

    我不建议使用中间件来编辑 REST-Framework 的响应。我认为你应该重载 REST-Framework 的默认 JSONRenderer。

    定义自定义渲染器:

    from rest_framework.renderers import JSONRenderer
    
    
    class CustomJsonRender(JSONRenderer):
    
        def render(self, data, accepted_media_type=None, renderer_context=None):
    
            if renderer_context:
                response = renderer_context['response']
                msg = "OK"
                code = response.status_code
                if isinstance(data, dict):
                    msg = data.pop('msg', msg)
                    code = data.pop('code', code)
                    data = data.pop('data', data)
                if code != 200 and data:
                    msg = data.pop('detail', 'failed')
                response.status_code = 200
                res = {
                    'code': code,
                    'msg': msg,
                    'data': data,
                }
                return super().render(res, accepted_media_type, renderer_context)
            else:
                return super().render(data, accepted_media_type, renderer_context)
    

    在你的 APiVIew 或 ViewSet 中使用它:

    class SimpleView(APIView):
        renderer_classes = (CustomJsonRender,)
        def get(self, request):
            # do something
            return Response({"id":"xx"})
    
    class SimpleViewSet(ModelViewSet):
        renderer_classes = (CustomJsonRender,)
        queryset = SomeModel.objects.all()
        serializer_class = SomeSerializer
    

    您也可以在设置中对其进行全局编辑:

    REST_FRAMEWORK = {
        "DEFAULT_RENDERER_CLASSES": ("compent.renders.CustomJsonRender",)
    }
    

    那么您的回复已更改。

    【讨论】:

    • 这种偏好的原因是什么?我想更容易拦截 b4 渲染....
    【解决方案3】:

    我感觉我找到了更清洁的解决方案。以下是我重写代码的方式:

    class RequestLogMiddleWare(object):
        def __init__(self, get_response):
           self.get_response = get_response
    
        def __call__(self, request):
           response = self.get_response(request)
           return response
    
        def process_template_response(self, request, response):
           if hasattr(response, 'data'): 
              response.data['detail'] = 'bla-bla-bla'
           return response
    

    【讨论】:

    • ValueError at ________Circular reference detected如何解决这个问题?
    【解决方案4】:
    class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.
    
    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
    
        response = self.get_response(request)
    
        res = str(response.content)
    
        res += "<h2>add some html!</h2>"
    
        response.content = bytes(res, encoding="UTF8")
    
        return response
    

    response.content类型是bytes,所以需要先转换成str

    向其中添加一些内容,将其转换为字节

    最后将其分配给 response.content。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-13
      • 2021-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多