这是 Fiber 最关键、也最容易翻车的部分。
官方 Welcome 和 Go Context 文档都在强调同一件事:
fiber.Ctx 以及从它拿到的很多值,只在当前 handler 生命周期内有效。处理完就会被复用。
这句话你必须咽下去。
#1. 为什么会这样
因为 Fiber 为了快,会尽量复用对象和底层缓冲区。
这就像快线城区为了吞吐量,不给每辆车都新造一套全新表单,而是:
- 办完就回收
- 下一辆车再复用
所以下面这种写法是危险的:
var saved string
app.Get("/users/:id", func(c fiber.Ctx) error {
saved = c.Params("id") // 危险:底层值可能被后续请求复用
return c.SendString(saved)
})更危险的是把 c 本身扔进 goroutine:
app.Get("/job", func(c fiber.Ctx) error {
go func() {
_ = c.Params("id") // 危险:handler 返回后,c 可能已被复用
}()
return c.SendStatus(fiber.StatusAccepted)
})#2. 正确姿势:只在当前 handler 内使用
app.Get("/users/:id", func(c fiber.Ctx) error {
id := c.Params("id")
return c.JSON(fiber.Map{"id": id})
})如果你必须把值带出当前 handler,就复制:
app.Get("/users/:id", func(c fiber.Ctx) error {
id := c.App().GetString(c.Params("id"))
go func(userID string) {
// 安全使用
println(userID)
}(id)
return c.SendStatus(fiber.StatusAccepted)
})如果你开启:
app := fiber.New(fiber.Config{
Immutable: true,
})则可以让上下文返回值变成可安全持久化的副本,但代价是性能会下降。
所以 Immutable 更像:
你把临时通行牌改成“一人一份正式复印件”,安全更高,速度略慢。
#3. Ctx 里到底装了什么
你可以把 fiber.Ctx 理解为“当前车辆的临时通行档案袋”,里面装着:
- 路径参数:
c.Params("id") - 查询参数:
c.Query("page") - Header:
c.Get("Authorization") - Cookie:
c.Cookies("token") - 请求体读取与绑定:
c.Bind()... - 响应状态:
c.Status(...) - 返回 JSON / 字符串 / 文件:
c.JSON(...)、c.SendString(...) - 中间件链继续执行:
c.Next() - 临时挂载值:
c.Locals(...)
#4. Locals 是站内临时标签,不是数据库
app.Use(func(c fiber.Ctx) error {
c.Locals("requestStart", time.Now())
return c.Next()
})
app.Get("/x", func(c fiber.Ctx) error {
start := c.Locals("requestStart").(time.Time)
_ = start
return c.SendStatus(fiber.StatusOK)
})它适合放:
- 当前请求的用户身份
- 请求开始时间
- 中间件传给下游的数据
- request id
- 访问控制结果
不适合放:
- 跨请求共享状态
- 全局缓存
- 长生命周期对象