Gunicorn预请求钩子中如何读取未读取的POST请求体?
Gunicorn pre_request钩子读取POST请求体的问题解决
问题场景
调试时想在Gunicorn级别通过pre_request钩子记录POST数据,写了如下代码:
def pre_request(worker, req): if req.query.startswith('action') and req.method == 'POST': print(req.body) # (1) request = copy.deepcopy(req.body) # (2) print(request.read().decode())
结果遇到两个问题:
- 用方法(1)直接打印
req.body后,触发错误:
..lib/python3.8/site-packages/gunicorn/http/wsgi.py", line 229, in should_close if self.status_code < 200 or self.status_code in (204, 304): AttributeError: 'Response' object has no attribute 'status_code'.
原因是req.body是类文件对象,打印时会读取缓冲区内容,指针移到末尾,后续业务流程再读取时就拿不到数据,导致处理异常。
- 用方法(2)尝试深拷贝
req.body时,触发错误:
..lib/python3.8/socket.py", line 272, in __getstate__ raise TypeError(f"cannot pickle {self.__class__.__name__!r} object") TypeError: cannot pickle 'socket' object
因为req.body内部关联了socket资源,这类对象无法被序列化拷贝。
环境版本:gunicorn19.9.0,Flask1.0.2
解决方法
方法1:读取后重置文件指针
读取请求体内容后,把指针重置到起始位置,确保后续流程还能正常读取:
def pre_request(worker, req): if req.query.startswith('action') and req.method == 'POST': # 读取请求体并解码 body = req.body.read().decode() print(body) # 将文件指针移回开头 req.body.seek(0)
这种方法简单直接,适合大多数常规请求场景;如果是超大文件上传,可能会有内存占用问题,需要额外处理。
方法2:自定义Gunicorn日志(推荐)
如果只是需要记录请求体到日志,没必要写钩子,直接扩展Gunicorn的日志功能更规范:
- 自定义日志类,添加请求体字段:
from gunicorn import glogging class CustomAccessLogger(glogging.Logger): def access(self, resp, req, environ, request_time): # 读取请求体 body = environ['wsgi.input'].read().decode() # 重置指针 environ['wsgi.input'].seek(0) # 把请求体存入环境变量,供日志格式调用 environ['request_body'] = body super().access(resp, req, environ, request_time)
- 启动Gunicorn时指定日志类,并配置日志格式:
gunicorn --logger-class your.module.CustomAccessLogger --access-log-format '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" "%(request_body)s"' your_app:app
方法3:在Flask层面处理(更简单)
如果不需要在Gunicorn底层操作,直接用Flask的请求钩子记录更省心,避开Gunicorn的底层对象限制:
from flask import Flask, request app = Flask(__name__) @app.before_request def log_post_data(): # 根据实际路径调整判断条件 if request.path.startswith('/action') and request.method == 'POST': print(request.get_data().decode())
内容的提问来源于stack exchange,提问作者Alex Bender
相关产品推荐
相关产品推荐

