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

用 ctx.Values() 传数据,别碰全局变量或 ctx.Request().Context()
中间件之间、中间件到 handler 之间传递请求级数据,唯一安全可靠的方式是 ctx.Values()。它线程安全、生命周期绑定单次请求、框架自动清理——你写进去,所有后续中间件和 handler 都能读,请求结束就清空。
常见错误包括:
- 用 package 级变量存
user_id或token:并发下数据串,A 请求的值被 B 请求覆盖 - 往
ctx.Request().Context()塞值:Iris 不管理它的生命周期,也不会透传到 handler 的ctx.Values(),纯属白写 - 在 struct 字段里临时存:无法跨中间件自动传递,且容易漏清,变成内存泄漏隐患
正确写法:
// 中间件 A
func authMiddleware(ctx iris.Context) {
token := ctx.GetHeader("Authorization")
userID := parseToken(token) // 假设已实现
ctx.Values().Set("user_id", userID)
ctx.Values().Set("auth_token", token)
ctx.Next()
}
// 中间件 B 或最终 handler
func logMiddleware(ctx iris.Context) {
id := ctx.Values().GetInt64("user_id") // 类型安全读取
if id > 0 {
log.Printf("request from user %d", id)
}
ctx.Next()
}
Party 分组复用数据要靠组级中间件统一注入
如果你用 app.Party("/api") 创建路由分组,并希望该组下所有路由都带 tenant_id 或 region,不能每个 handler 里重复解析,也不能靠 URL 参数硬塞。
应该把解析逻辑封装进一个中间件,注册到 Party 上:
api := app.Party("/api")
api.Use(func(ctx iris.Context) {
tenant := ctx.GetHeader("X-Tenant-ID")
if tenant != "" {
ctx.Values().Set("tenant_id", tenant)
}
ctx.Next()
})
api.Get("/users", userHandler) // 自动能读 ctx.Values().GetString("tenant_id")
api.Post("/orders", orderHandler)
注意:不要在 Party 外层用 app.Use() 注册这个逻辑,否则会污染非 /api 路由;也不要试图用 ctx.Params().Get() 或 ctx.URLParam() 替代——它们只管路由参数,不承载业务上下文。
别往 ctx.Values() 存大对象或深层嵌套结构
ctx.Values() 是内存键值对,不是缓存池。存太大的东西会拖慢 GC、增加内存压力,尤其在高 QPS 场景下容易成为瓶颈。
以下做法要避免:
- 存原始
[]byte(比如整个请求 body) - 存未序列化的
map[string]interface{}或嵌套多层的 struct 实例 - 存数据库连接、文件句柄等资源对象(根本不是 request-scoped 的)
推荐替代方案:
- 只存 ID、状态码、布尔标记等轻量值(如
"is_admin": true、"trace_id": "abc123") - 大对象转成 ID 后存,真正要用时再查(例如存
"user_profile_id": 456,handler 里按需查库) - 需要共享复杂状态?走外部存储(Redis)或 session,别强塞进请求上下文
为什么改 ctx.Request().URL.Query() 传参行不通
有人想“偷偷”往 query 里加参数,比如在中间件里写 ctx.Request().URL.RawQuery += "&debug=1",指望后续 handler 调 ctx.URLParam() 能读到。这完全无效。
原因很直接:
- Iris 在路由匹配前就已解析好 query 并缓存到内部字段,
ctx.URLParam()读的是缓存,不是实时URL对象 -
ctx.Request().URL是个副本,改它等于改了个纸面记录,不影响框架行为 - 一旦中间件触发重定向(
ctx.Redirect()),原始 query 会被丢弃,你塞的 fake 参数也跟着消失
真要透传,只有一条路:ctx.Values().Set()。跨请求持久化?那是 session 或 JWT payload 的事,不是 ctx.Values() 的职责。
最易忽略的一点:ctx.Values() 的 key 名最好统一封装成常量,避免拼错或大小写不一致导致读不到——这问题在线上很难 debug。


















