自定义中间件必须严格签名 func(*fiber.ctx) error,否则运行时 panic;需用 c.next() 或显式返回响应;应通过参数化、c.locals 和显式注册实现复用与安全。

自定义中间件必须返回 error 类型且签名严格匹配 func(*fiber.Ctx) error
这是最常导致 panic 的硬性约束。Fiber 不做类型擦除或自动适配,app.Use() 或路由参数中传入的函数若签名不符(比如少个 error 返回值、参数不是 *fiber.Ctx),编译能过但运行时会直接 panic:“invalid handler”。
常见错误现象:写成 func(c *fiber.Ctx)(无返回)、func(c fiber.Ctx)(指针缺失)、func(c *fiber.Ctx) string(返回类型错)——全部挂。
- 务必显式写全签名:
func(c *fiber.Ctx) error - 即使不打算中断流程,也得写
return c.Next(),不能只写c.Next() - 若需提前终止(如鉴权失败),用
return c.Status(401).SendString("unauthorized"),而不是c.Status(401); return nil(nil 会被 Fiber 当作成功,继续执行后续中间件)
复用性关键:把可变逻辑抽成参数,避免硬编码
直接写死逻辑的中间件无法跨项目/路由复用。比如一个日志中间件,如果把日志前缀写死为 "[API]",就无法用在 /health 路径下。
正确做法是把变化点做成函数参数,返回闭包形式的中间件:
func WithPrefix(prefix string) fiber.Handler {
return func(c *fiber.Ctx) error {
log.Printf("%s %s %s", prefix, c.Method(), c.Path())
return c.Next()
}
}
使用时:app.Use("/api", WithPrefix("[API]"))、app.Get("/health", WithPrefix("[HEALTH]"), healthHandler)。
- 不要在中间件内部调用
log.Println等全局输出,应接受io.Writer或 logger 实例作为参数 - 涉及配置的(如 JWT 密钥、限流窗口),统一通过结构体传入,而非从环境变量硬读(否则测试难、部署耦合)
- 避免在闭包里捕获外部变量引用(如
var cfg Config; return func(c *fiber.Ctx) error { ... cfg.Timeout ... }),多实例时易被意外覆盖
路径作用域和执行时机必须靠 app.Use() 前缀或路由级传参控制
很多人误以为“写个中间件函数就能自动生效”,其实它只是个函数,必须显式注册。而注册方式直接决定它何时、对谁生效。
常见错误现象:把中间件函数定义好后忘了 app.Use(mw),或者写了 app.Use(mw) 却没加路径前缀,结果连 /favicon.ico 都被拦截,拖慢前端加载。
- 只对 API 路径生效 →
app.Use("/api", mw)(注意前缀带/) - 只对某条 POST 路由生效 →
app.Post("/submit", mw, handler) - 想让中间件跳过某些子路径(如不处理静态资源)→ 必须在中间件内部手动判断:
if strings.HasPrefix(c.Path(), "/assets/") { return c.Next() },Fiber 不提供“排除路径”配置项
状态传递要用 c.Locals,别依赖闭包变量或全局 map
中间件之间需要共享数据(比如解析出的用户 ID),不能靠外部变量暂存,否则并发请求会互相污染。
Fiber 提供 c.Locals 作为请求生命周期内的安全存储,类型是 map[string]interface{},只对该次请求可见:
func AuthMiddleware() fiber.Handler {
return func(c *fiber.Ctx) error {
token := c.Get("Authorization")
userID, err := parseToken(token)
if err != nil {
return c.Status(401).SendString("invalid token")
}
c.Locals("user_id", userID) // ✅ 安全写入
return c.Next()
}
}
func ProtectedHandler(c *fiber.Ctx) error {
userID := c.Locals("user_id") // ✅ 安全读取
return c.JSON(fiber.Map{"user_id": userID})
}
- 不要用
var currentUser *User这类包级变量存用户信息 -
c.Locals是 map,key 建议用常量字符串(如const UserKey = "user"),避免拼写错误 - 如果需要强类型,可封装一层:
c.Locals(UserKey, user); u := c.Locals(UserKey).(User),注意加类型断言检查











