中间件函数签名必须返回fiber.Handler,即func(*fiber.Ctx) error;忽略error返回值会导致类型不匹配编译错误,正确写法是用闭包返回该签名,并通过c.Next()或显式响应终止流程。

中间件函数签名必须返回 fiber.Handler
写 Fiber 中间件时,最常踩的坑是函数签名不对——它不是普通函数,必须符合 fiber.Handler 类型定义:func(*fiber.Ctx) error。返回值不能省略,且必须是 error;如果忽略返回值或写成 func(*fiber.Ctx),编译会报类型不匹配,Fiber 也无法注册。
常见错误现象:启动时报 cannot use xxx (type func(*fiber.Ctx)) as type fiber.Handler。
- 正确写法:
func MyMiddleware() fiber.Handler { return func(c *fiber.Ctx) error { // 逻辑 return c.Next() // 或 c.SendStatus(200) 等 } } - 不要在中间件里直接调用
c.Next()后再写后续逻辑(除非你明确想在响应后执行),因为c.Next()是阻塞式等待下游处理完成的 - 若中间件需提前终止流程(如鉴权失败),必须显式返回
return c.Status(401).SendString("unauthorized"),否则会继续往下走
如何读取和修改请求上下文(c.Locals vs c.Context().Value)
Fiber 推荐用 c.Locals 存储中间件间传递的数据,而不是原生 context.Context 的 Value。前者性能更好、API 更简洁,且能被 Fiber 日志、恢复中间件等正确识别。
-
c.Locals("user_id", 123)设置值,c.Locals("user_id")取值(返回interface{},需类型断言) - 避免用
c.Context().SetValue:Fiber 不保证该 context 在整个生命周期中不被替换,Locals才是稳定挂载点 - 注意键名冲突:多个中间件用相同字符串键写入
Locals会覆盖,建议加前缀,比如"auth.user"、"trace.id"
全局中间件和路由组中间件的区别与使用时机
app.Use() 注册的是全局中间件(匹配所有路径,包括静态文件),而 router := app.Group("/api") 后的 router.Use() 只作用于该前缀下的路由。选错位置会导致中间件重复执行或漏执行。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
立即学习“go语言免费学习笔记(深入)”;
- 日志、CORS、恢复 panic 这类基础设施中间件,适合用
app.Use() - 权限校验、租户解析这类业务相关中间件,应绑定到具体路由组,例如:
api := app.Group("/api") api.Use(AuthMiddleware()) // 仅 /api/* 路由触发 api.Get("/users", handler) - 静态文件中间件(
app.Static())默认不经过app.Use()链,除非显式配置Next: true,否则你的日志中间件看不到静态资源请求
中间件里调用 c.Next() 的实际行为和陷阱
c.Next() 并非“跳转到下一个中间件”,而是同步执行后续中间件链 + 最终路由处理器,并等待其返回。它本身不返回控制权给你,后续代码是在下游全部处理完(响应已写入)之后才运行的。
- 典型误用:在
c.Next()后读取c.Response().StatusCode()做判断——这是可行的,但要注意此时响应体可能已被写出,不能再调用c.SendXXX - 想做“响应后钩子”(如记录耗时、审计日志),可以:
start := time.Now() err := c.Next() duration := time.Since(start) log.Printf("path=%s status=%d duration=%v", c.Path(), c.Response().StatusCode(), duration) - 若下游 panic,且你没用
fiber.New()配置Recover中间件,c.Next()会直接返回 panic 错误,你的中间件需检查err != nil并决定是否透传
真正容易被忽略的是:中间件顺序不可逆,app.Use(A, B) 和 app.Use(B, A) 行为完全不同;鉴权中间件必须放在日志中间件之前,否则未授权请求也会被记日志。

















