应使用 iris 的 ctx.values() 传递请求级数据,它线程安全、生命周期绑定单次请求、自动清理;禁止用全局变量、ctx.request().context() 或修改 url.query();party 分组复用数据需通过组级中间件统一写入 ctx.values()。

怎么在 handler 之间传递数据,而不是靠全局变量
不能用 package 级变量存请求级数据,因为并发下会错乱。Iris 提供了 ctx.Values() 这个线程安全的键值存储,生命周期绑定单次请求,从中间件到最终 handler 都能读写。
常见错误是把数据塞进 ctx.Request().Context() 或自定义 struct 字段——前者不被 Iris 生命周期管理,后者无法跨中间件自动传递;只有 ctx.Values() 是框架原生支持、自动清理的方案。
- 写入:
ctx.Values().Set("user_id", 123)或ctx.Values().Set("auth_token", token) - 读取:
id := ctx.Values().GetInt64("user_id")或token := ctx.Values().GetString("auth_token") - 类型安全建议:用
ctx.Values().Get+ 类型断言,或直接用GetInt64/GetString等封装方法,避免 panic
为什么不能在中间件里改 ctx.Request().URL.Query() 来传参
修改 ctx.Request().URL.Query() 不会影响后续 handler 对查询参数的解析,因为 Iris 在路由匹配前就已将 query 解析为 ctx.URLParam 和 ctx.FormValue 的底层缓存;手动改 URL 对象只是改了个副本,不会触发重解析。
更严重的是,如果中间件做了重定向(ctx.Redirect),原始 query 会被丢弃,而你塞进 ctx.Values() 的数据会随新请求消失——这是设计使然,不是 bug。
- 正确做法:需要透传的数据,统一走
ctx.Values();需要持久化或跨请求的,走 session 或外部存储 - 注意:不要在
ctx.Values()存大对象(如 []byte、map 嵌套深),影响 GC 和内存占用 - 调试时可用
ctx.Values().GetAll()查看当前所有键,但生产环境避免调用
Party 分组内多个路由如何复用同一份预处理数据
Party 本身不提供“组级上下文”,但你可以把初始化逻辑写成中间件,在 Party 创建后立即注册,这样组内所有路由都会经过它。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
例如 admin 组需要统一校验权限并加载管理员信息,不要每个 handler 都重复查 DB,而是:
- 定义中间件:
adminLoadMiddleware := func(ctx iris.Context) { user, err := loadAdminUser(ctx); if err != nil { ctx.StatusCode(401); return }; ctx.Values().Set("admin_user", user); ctx.Next() } - 挂载:
admin := app.Party("/admin").Use(adminLoadMiddleware) - 后续所有
admin.Get("/dashboard", ...)都能直接读ctx.Values().Get("admin_user")
这种写法比在每个 handler 里手动调用 loadAdminUser 更可靠,也方便统一加日志或熔断逻辑。
容易忽略的坑:Values 数据不会自动序列化进日志或 trace
ctx.Values() 是纯内存结构,Iris 默认日志、Prometheus metrics、OpenTracing 都不会采集它里面的内容。如果你依赖这些数据做监控(比如 “按 user_id 统计 API 调用量”),必须显式提取并注入指标标签或日志字段。
例如在中间件末尾加一句:ctx.Application().Logger().Infof("user_id=%d path=%s", ctx.Values().GetInt64("user_id"), ctx.Path()),否则排查问题时会发现日志里全是空 user_id。
另外,Values 中存的 struct 如果没实现 String() 方法,用 %v 打印出来可能是一堆地址,调试困难——建议对关键业务对象补上可读输出。










