fiber中间件通过c.locals("key", value)存数据,handler中用c.locals("key").(type)类型断言获取;需确保中间件注册在路由前、调用c.next(),且键名统一定义避免拼写错误。

中间件里怎么把数据传给后续 handler
用 c.Locals,不是 c.Set 也不是全局变量。Fiber 的 *fiber.Ctx 是复用对象,每次请求都会重置,Locals 就是专为“本次请求内跨中间件/路由共享数据”设计的 map。
常见错误:在中间件里写 c.Locals("user_id", 123),然后在 handler 里直接用 c.Locals("user_id") —— 这会返回 interface{},不转型就 panic;或者忘了加 & 导致结构体复制后修改无效。
-
c.Locals只对当前请求有效,不同请求间完全隔离 - 存进去的是
interface{},取出来必须类型断言:id := c.Locals("user_id").(int)或更安全的if id, ok := c.Locals("user_id").(int); ok { ... } - 别往
Locals存大对象(比如未序列化的 *sql.DB),避免 GC 压力;优先存 ID、token、配置标志等轻量值 - 如果要存指针(如
*User),确保它生命周期不超过当前请求 —— 别把它传进 goroutine
为什么 c.Locals 在 handler 里取不到值
执行顺序错了。中间件必须通过 app.Use() 注册,且调用位置必须在对应路由注册之前;否则 c.Next() 没走到你设 Locals 的那步,handler 就先跑了。
典型误写:
app.Get("/api/user", authMW, userHandler) // authMW 是函数,不是 app.Use() 调用
app.Use(loggingMW)
这样 loggingMW 确实会执行,但 authMW 是路由级中间件,只对这个路由生效,且在 loggingMW 之后运行 —— 如果 loggingMW 依赖 user_id,它就拿不到。
- 全局共享数据 → 用
app.Use(mw),且放在所有app.Get/Post之前 - 仅某几个路由需要 → 用
app.Use("/api/*", mw)做路径前缀匹配 - 单个路由定制 →
app.Get("/x", mwA, mwB, handler),注意顺序:mwA 先于 mwB 执行 - 检查是否漏了
c.Next():没调它,后续中间件和 handler 根本不执行,Locals自然没机会设
struct 字段怎么安全塞进 Locals 并复用
别直接塞结构体值,尤其含 slice/map/chan 的 —— Locals 存的是副本,后续修改不影响原值,容易误判状态。推荐存指针或 ID,handler 里按需查。
例如鉴权中间件解析 JWT 后,应该:
// ✅ 推荐:存 ID 和指针(确保生命周期可控)
user := &User{ID: 123, Name: "alice"}
c.Locals("user", user)
c.Locals("user_id", user.ID)
// ❌ 避免:存结构体值 + 后续改字段
u := User{ID: 123}
c.Locals("user", u)
u.Name = "bob" // 这个改不动 Locals 里的副本
- 如果 handler 需要修改用户信息(如更新 last_seen),直接改
c.Locals("user").(*User)指向的对象即可 - 若结构体较大(> 128B),考虑只存
user_id,handler 里再查 DB,避免内存浪费 - 不要在中间件里
defer清空Locals—— Fiber 会在请求结束时自动回收整个Ctx,手动清可能干扰其他中间件
Locals 和 Context.Value 有什么区别
Fiber 不鼓励用 context.WithValue。因为 *fiber.Ctx 本身已封装了请求上下文,Locals 是其内置、零分配、类型安全的替代方案;而 context.Value 是通用接口,类型断言麻烦,还容易因 key 冲突或泄漏导致 bug。
实际差异:
-
c.Locals键是string,值任意,底层是sync.Map(v2)或预分配 map(v3),读写快 -
context.WithValue键建议用自定义类型防冲突,但 Fiber 的中间件链里没人帮你做这层封装 - IDE 补全支持:
c.Locals可被 gopls 识别为 Fiber 上下文方法;context.Value是泛型,补全无提示 - 性能:基准测试显示
c.Locals比嵌套context.WithValue快 2–3 倍,且无内存逃逸
真正容易被忽略的是:Locals 的键名一旦拼错(比如 "user_id" vs "userid"),运行时不会报错,只会返回 nil 或零值 —— 建议统一定义常量,比如 const CtxKeyUserID = "user_id"。











