我有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'}
我在中间件的调用方法中放置了一个断点,以确保该函数真正执行,并且没问题。 response.data["details"]
只是不会改变它的价值。有谁知道这是什么原因?
答案 0 :(得分:10)
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框架浏览器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
答案 1 :(得分:3)
我不建议使用中间件来编辑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",)
}
然后您的回复已更改。
答案 2 :(得分:2)
我觉得我找到了更清洁的解决方案。这是我重写代码的方式:
class RequestLogMiddleWare(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
def process_template_response(self, request, response):
if hasattr(response, 'data'):
response.data['detail'] = 'bla-bla-bla'
return response