fiber.Handler类型签名必须为func(*fiber.Ctx) error,省略error返回值会导致编译错误“cannot use xxx as type fiber.Handler”;正确写法是闭包返回该签名,并用return c.Next()或显式响应终止流程。

fiber.Handler 类型签名不能省略 error 返回值
写中间件函数时,最常卡住的地方是类型不匹配:编译报 cannot use xxx (type func(*fiber.Ctx)) as type fiber.Handler。根本原因是 fiber.Handler 定义为 func(*fiber.Ctx) error,必须显式返回 error —— 即使你什么都不做,也得写 return nil 或 return c.Next()。
常见错误写法:func(c *fiber.Ctx) { ... }(缺返回值)、func(c *fiber.Ctx) bool { ... }(类型错)。
- 正确结构一定是闭包返回
fiber.Handler:用func() fiber.Handler { return func(c *fiber.Ctx) error { ... } } - 调用
c.Next()本身返回error,所以可直接return c.Next() - 提前终止时,比如鉴权失败,必须
return c.Status(401).SendString("unauthorized"),不能只写响应不return
用 c.Locals 而不是 context.WithValue 传数据
Fiber 的 fiber.Ctx 自带请求作用域的键值存储,推荐用 c.Locals 存用户 ID、租户 ID 等中间件间传递的数据。它比原生 context.Context 更轻、更稳定,且被 Fiber 日志、panic 恢复等内置中间件识别。
别写 c.Context().WithValue(key, val) —— 这个值在下游 handler 里取不到,因为 Fiber 并未将 c.Context() 与 c.Locals 同步。
- 存值:
c.Locals("user_id", 123) - 取值:
uid := c.Locals("user_id"),返回interface{},需类型断言:uid.(uint) - 键名建议加前缀防冲突,比如
"auth.user_id"、"tenant.id" - 避免多个中间件用相同字符串键反复写入,会覆盖
中间件注册顺序决定执行链,logMW 必须第一个
Fiber 没有“优先级”或“拦截器级别”概念,app.Use() 的调用顺序就是请求进入时的执行顺序。响应阶段则倒序执行。这意味着顺序错了,日志可能打不全、panic 可能没被捕获、鉴权可能被绕过。
典型错误:把 recover.New() 写在 logger.New() 后面 → panic 发生时,logger 的响应日志根本不会打印。
- 推荐顺序:
app.Use(logger)→app.Use(recover)→app.Use(cors)→app.Use(jwt)→app.Use(tenant) - 路由组中间件(如
api := app.Group("/api"); api.Use(auth))会在所有全局app.Use()之后执行 - 单个路由上挂中间件:
app.Get("/admin", auth, audit, handler),它排在全局中间件之后、该 handler 之前 - 漏掉
return或误调c.Next()都会导致后续逻辑意外执行
黑名单/权限/租户类中间件必须显式终止流程
这类中间件的核心动作不是“继续”,而是“阻断”。一旦判定非法(IP 黑名单命中、权限不足、租户 ID 无效),就必须立即返回 HTTP 响应并 return,绝不能调 c.Next()。
常见翻车点:写了 c.Status(403).JSON(...) 却忘了 return,结果 handler 仍被执行,可能重复写响应头、泄露数据或触发副作用。
- 判断失败时,标准写法:
return c.Status(403).JSON(fiber.Map{"error": "forbidden"}) - 黑名单要读原始 body?用
c.Body()或c.Request().Body(),别碰c.BodyParser(),否则 body 被消费后下游拿不到 - 租户中间件校验失败?
return c.Status(400).JSON(fiber.Map{"error": "invalid tenant"}) - 权限检查走缓存+DB 双查,别硬编码角色字段;通配符权限如
user:delete:*要靠authz.Can()内部支持
实际写中间件时,最容易被忽略的是「终止语义」——Fiber 不靠 next() 控制放行,而靠是否调用 c.Next() + 是否 return 来决定流程走向。一个没 return 的错误响应,和一个多余的 c.Next(),都足以让安全策略形同虚设。


















