You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

关键说明

  1. DRF的Response对象自带render()方法,调用后会完成渲染流程并生成content属性(字节形式的响应内容);
  2. Django原生JsonResponse/HttpResponse的content属性直接存储渲染后的字节内容,无需额外处理;
  3. 重复调用response.render()不会引发问题,即使响应已经被渲染过。

内容的提问来源于stack exchange,提问作者Adhishraya Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:23:10