Flask中不能用模块级全局变量存请求数据,因并发时会相互覆盖;应使用请求上下文对象g,在@app.before_request中初始化,通过@app.teardown_request清理,其生命周期绑定单次请求且线程安全。

为什么不能直接用全局变量存请求数据
Flask 是多线程/协程模型,g 不是普通全局变量,而是每个请求独立的上下文容器。直接用模块级变量(比如 current_user_id = None)会导致 A 请求覆盖 B 请求的数据,尤其在并发场景下必出错。
常见错误现象:用户 A 登录后,用户 B 的接口返回了 A 的用户名;日志里出现混杂的 trace_id 或用户 ID。
真正该用的只有 g —— 它由 Flask 自动绑定到当前请求生命周期,请求结束自动清空。
注意:g 在请求外访问会抛 RuntimeError: Working outside of application context.,不是所有地方都能用。
g对象怎么初始化和赋值才安全
必须在请求上下文内操作,最稳妥的位置是 @app.before_request 或中间件函数里。
不要在模块顶层、配置文件、或 __init__.py 中给 g 赋值。
实操建议:
- 从
request.headers提取X-Request-ID并存入g.request_id - 解析 JWT token 后把用户信息塞进
g.user,而不是反复 decode - 避免存大对象(如数据库连接、文件句柄),
g只适合轻量元数据
示例:
@app.before_request<br>def before_request():<br> g.request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))<br> g.start_time = time.time()
如何在视图、模板、日志中一致使用g里的数据
g 在整个请求链路中可穿透:视图函数、render_template、自定义 Jinja2 过滤器、甚至 logging 的 Formatter 都能读取。
但要注意两点:
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
- 模板里直接用
{{ g.request_id }},无需 import - 日志中若想打
g.request_id,得用logging.LoggerAdapter或自定义Filter注入,不能在basicConfig里硬写 - 异步任务(Celery、threading.Thread)里
g不可用——它只属于主线程的请求上下文
性能影响极小,g 底层是 LocalProxy + 线程局部存储,无锁、无拷贝。
g对象的生命周期和清理陷阱
g 生命周期严格绑定请求:从 before_request 开始,到 teardown_request 结束。
容易被忽略的是:如果用了 @app.teardown_request,它**一定执行**,哪怕视图抛异常或 return 提前退出。
所以清理逻辑(比如关闭临时缓存、重置状态)放这里最可靠:
@app.teardown_request<br>def teardown_request(exception):<br> if hasattr(g, "db_session"):<br> g.db_session.close()
别指望 finally 块或上下文管理器自动兜底——g 本身不提供自动销毁钩子,全靠你手动配 teardown。
另外,测试时容易漏掉上下文:单元测试里用 app.test_request_context() 才能激活 g,否则读 g.xxx 直接报错。

















