Iris 的 context.Context 是请求级、线程安全、自动生命周期管理的共享载体,不是全局变量,也不是手动传参;用它共享业务数据,核心就三件事——存、取、不跨请求泄漏。

直接说结论:Iris 的 context.Context 是请求级、线程安全、自动生命周期管理的共享载体,不是全局变量,也不是手动传参;用它共享业务数据,核心就三件事——存、取、不跨请求泄漏。
为什么不能用全局变量或闭包存用户 ID 或 trace_id
很多人一上来想“我定义个 var currentUser *User 全局变量,中间件里赋值,后面 handler 里直接读”,这在 Iris(以及所有并发 HTTP 框架)里是危险操作:
- Go 的 HTTP server 默认多协程处理请求,
currentUser会被多个请求同时读写,结果不可预测 - 没有生命周期绑定,上一个请求设的值可能被下一个请求意外读到(即“串台”)
- Iris 的
context.Context对象本身是每次请求新建的,天然隔离
怎么在中间件里往 context 写入业务数据
用 ctx.Values().Set(),这是 Iris 提供的键值对存储区,专为请求上下文设计:
app.Use(func(ctx iris.Context) {
// 从 header 或 token 解析用户
token := ctx.GetHeader("Authorization")
user, err := parseToken(token)
if err != nil {
ctx.StatusCode(401)
ctx.WriteString("unauthorized")
return
}
// ✅ 正确写法:绑定到当前 ctx
ctx.Values().Set("user", user)
ctx.Values().Set("trace_id", uuid.NewString())
ctx.Next()
})
-
ctx.Values()返回的是*context.Map,内部用sync.RWMutex保证并发安全 - 键名建议用常量避免拼写错误,比如
const CtxKeyUser = "user" - 不要用
ctx.SetValues()(已废弃),也不要尝试把整个 struct 序列化成字符串再塞进去
handler 里怎么安全读取 context 中的数据
用 ctx.Values().Get() 或带类型的 ctx.Values().GetReadOnly():
app.Get("/profile", func(ctx iris.Context) {
// ✅ 安全读取,类型断言要判空
if user, ok := ctx.Values().Get("user").(*User); ok && user != nil {
ctx.JSON(iris.Map{"name": user.Name, "id": user.ID})
return
}
ctx.StatusCode(403)
ctx.WriteString("forbidden")
})
-
Get()返回interface{},必须做类型断言;GetReadOnly()更推荐,它返回只读视图,防止误写 - 如果 key 不存在,
Get()返回nil,不做判空直接解引用会 panic - 别在 goroutine 里异步读取
ctx.Values()—— 请求结束,ctx就被回收了,此时读是未定义行为
容易被忽略的坑:中间件顺序、子路由继承与生命周期
Context 数据不是“写了就永远在”,它的可见性严格受中间件注册顺序和路由分组影响:
- 只有在
ctx.Next()之前写入的值,下游中间件和最终 handler 才能读到 - 子路由组(
app.Party("/api"))默认继承父级ctx.Values(),但如果你在子组里调用Use()新增中间件,它写入的值不会反向透出到父组 -
ctx对象在请求结束时自动释放,你不需要手动清理Values(),但也不该把它保存成全局指针或塞进 long-running goroutine - 如果用了
ctx.Clone()(比如在 WebSocket 升级后),新 clone 出的ctx不会自动继承原Values(),需显式复制


















