如何在DRF视图装饰器中获取Response对象的响应内容
解决DRF类视图装饰器记录响应内容的报错问题
问题背景
我有一个用于Django REST Framework(DRF)类视图的装饰器LogRequest,代码如下:
class LogRequest: def __init__(self, log_success=True, methods_to_log=None): self.log_success = log_success self.methods_to_log = methods_to_log if methods_to_log is not None else ["post"] def __call__(self, view_class): view_class.methods_to_log = self.methods_to_log view_class.log_success = self.log_success for method_name in self.methods_to_log: if hasattr(view_class, method_name): method = getattr(view_class, method_name) decorated_method = self.wrap_method(method) setattr(view_class, method_name, decorated_method) return view_class def wrap_method(self, method): @wraps(method) def wrapped_method(self, request, *args, **kwargs): method_name = request.method.lower() if method_name in getattr(self, "methods_to_log", []): log = Log(path=request.path, method=method_name) log.request_body = Log.parse_request_body(request) try: response = method(self, request, *args, **kwargs) except Exception as e: tb = traceback.format_exc() log.error_traceback = tb log.status_code = 500 log.response_body = str(e) log.save() return JsonResponse({"error": str(e)}, status=500) if self.log_success or response.status_code >= 400: log.status_code = response.status_code if 'application/json' in response.get('Content-Type', ''): response_content = response.rendered_content.decode("utf-8") log.response_body = response_content log.save() return response return method(self, request, *args, **kwargs) return wrapped_method
将装饰器应用在类视图上的代码:
@LogRequest(log_success = True,methods_to_log=["post","get"]) class TestDecoView(generics.GenericAPIView): permission_classes = (AllowAny,) def post(self,request,*args, **kwargs): return JsonResponse({"message":"OK"},status=403) def get(self,request,*args, **kwargs): raise Exception("HAHA")
问题:当log_success为True时,记录成功或4xx响应会报错'JsonResponse' object has no attribute 'rendered_content'或'Response' object has no attribute 'rendered_content',异常日志正常,但响应日志无法生成。尝试手动设置accepted_renderer后无报错,但响应内容为空。
解决方案
问题根源在于:
- DRF的
Response对象在视图返回时还未经过渲染流程,没有rendered_content属性; - Django原生的
JsonResponse/HttpResponse属于HttpResponse子类,本身就没有rendered_content属性。
需要修改响应内容的获取逻辑,区分两种响应类型处理:
修改后的完整wrap_method方法
def wrap_method(self, method): @wraps(method) def wrapped_method(self, request, *args, **kwargs): method_name = request.method.lower() if method_name in getattr(self, "methods_to_log", []): log = Log(path=request.path, method=method_name) log.request_body = Log.parse_request_body(request) try: response = method(self, request, *args, **kwargs) except Exception as e: tb = traceback.format_exc() log.error_traceback = tb log.status_code = 500 log.response_body = str(e) log.save() return JsonResponse({"error": str(e)}, status=500) if self.log_success or response.status_code >= 400: log.status_code = response.status_code if 'application/json' in response.get('Content-Type', ''): # 区分DRF Response和Django原生响应 if hasattr(response, 'render'): # DRF Response需要手动触发渲染 response.render() response_content = response.content.decode("utf-8") else: # Django原生响应直接读取content属性 response_content = response.content.decode("utf-8") log.response_body = response_content log.save() return response return method(self, request, *args, **kwargs) return wrapped_method
关键说明
- DRF的
Response对象自带render()方法,调用后会完成渲染流程并生成content属性(字节形式的响应内容); - Django原生
JsonResponse/HttpResponse的content属性直接存储渲染后的字节内容,无需额外处理; - 重复调用
response.render()不会引发问题,即使响应已经被渲染过。
内容的提问来源于stack exchange,提问作者Adhishraya Sharma
相关产品推荐
相关产品推荐

