Django REST Framework日志过滤器中无法访问request.data如何解决
错误原因
你的代码触发RawPostDataException的核心逻辑是:
- Django原生HttpRequest的请求体数据流是一次性的,读取后就会被消费无法二次读取
- DRF的Request对象的
data属性首次访问时会解析请求体流,解析完成后流就为空 - 你在日志Filter中每次都重新实例化DRF Request对象,这个新实例没有缓存之前视图已经解析好的请求体数据,访问
request.data时会尝试重读已经被消费的流,就会抛出异常
解决方案
最稳妥的方案是通过中间件提前缓存解析好的请求体,避免重复读取流:
第一步:新增请求体缓存中间件
# 可放在对应app的middlewares.py文件中 from django.utils.deprecation import MiddlewareMixin from rest_framework.request import Request from rest_framework.settings import api_settings class CacheRequestBodyMiddleware(MiddlewareMixin): def process_request(self, request): # 跳过文件上传类请求,避免大文件占用过多内存 if request.content_type.startswith('multipart/'): request.cached_data = {} return # 仅解析一次请求体,缓存到原生HttpRequest对象上 drf_request = Request( request, parsers=[parser() for parser in api_settings.DEFAULT_PARSER_CLASSES], authenticators=[auth() for auth in api_settings.DEFAULT_AUTHENTICATION_CLASSES] ) try: request.cached_data = drf_request.data except Exception: # 解析失败时给空值,避免影响正常业务逻辑 request.cached_data = {}
第二步:注册中间件
在settings.py的MIDDLEWARE列表最顶部添加该中间件:
MIDDLEWARE = [ '你的应用名.middlewares.CacheRequestBodyMiddleware', # 放在列表第一位 'django.middleware.security.SecurityMiddleware', # 其他原有中间件保持不变 ]
第三步:修改日志Filter逻辑
直接读取缓存好的请求体数据,不需要再实例化DRF Request:
import logging class RequestInfoFilter(logging.Filter): def filter(self, record): request = record.request record.method = request.method record.query_params = request.GET.dict() # 直接拿缓存好的data,不会重读流 record.data = request.cached_data return True
补充说明
如果不想新增中间件,也可以在原有Filter中添加异常捕获兜底,避免日志逻辑影响业务,该方案仅适合临时调试使用,缺陷是如果视图已经读取过请求体,日志中就拿不到请求数据:
import logging from rest_framework.request import Request from rest_framework.settings import api_settings class RequestInfoFilter(logging.Filter): def filter(self, record): parsers = [parser() for parser in api_settings.DEFAULT_PARSER_CLASSES] authenticators = [ auth() for auth in api_settings.DEFAULT_AUTHENTICATION_CLASSES] request = Request( record.request, parsers=parsers, authenticators=authenticators) record.method = request.method record.query_params = request.query_params try: record.data = request.data except Exception: record.data = {} return True
内容的提问来源于stack exchange,提问作者Indy
相关产品推荐
相关产品推荐

