Editing response content in Django middleware

Viewed 7750

I have Django 1.10 project and the following user-defined middleware

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

and a REST-endpoint view:

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

So I would expect the final response on client-side to be:

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

However, what I see is:

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

I put a breakpoint in the middleware's call method to make sure that the function really is executed, and it's ok. response.data["details"] just won't change it's value. Anyone knows what's the reason for this ?

4 Answers

I don't suggest editing REST-Framework's Response with using middleware. I think you should overload REST-Framework's default JSONRenderer.

Define a custom renderer:

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)

Using it in you APiVIew or 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

You can edit it golbal in settings as well:

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

Then your response has been changed.

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 type is bytes, so you need first convert it to str,

add something to it, convert it to bytes

and finally assign it to the response.content.

Related