Fiber无全局错误处理器,必须用defer+recover中间件捕获panic并提前保存ctx信息,业务error需显式判断并统一返回;c.Status()后必须return,否则状态码被覆盖。

Fiber 没有内置“全局 error handler”机制,所有错误必须显式捕获并响应;靠 @app.errorhandler 这类 Flask 风格装饰器会直接失效。
为什么 app.Use() 中间件是唯一可靠入口
Fiber 的错误处理完全依赖中间件链的控制流,没有类似 Flask 的 @app.errorhandler 或 Express 的 app.use((err, req, res, next) => {}) 语法。一旦 panic 发生(比如 json.Unmarshal 崩溃、空指针解引用),进程直接退出——Fiber 不拦截 runtime panic。
- 你只能在中间件里用
defer + recover()捕获 panic,且必须放在中间件最外层 - 业务逻辑中抛出的 error(如数据库查询失败)不会自动冒泡,必须手动传给
c.Status().Send()或类似响应方法 - 注册顺序至关重要:
app.Use(recoverMiddleware)必须放在所有其他中间件之前,否则 panic 会绕过它
如何写一个真正兜住 panic 的 recover 中间件
别只写 recover() 就完事。Fiber 的 ctx 在 panic 后已不可用,必须在 defer 里提前保存关键信息(如路径、方法),否则日志里只剩空 request。
func Recover() fiber.Handler {
return func(c *fiber.Ctx) error {
defer func() {
if r := recover(); r != nil {
// ⚠️ c.Path() 和 c.Method() 必须在 panic 前读取,panic 后 c 可能已损坏
path := c.Path()
method := c.Method()
status := 500
// 记录 panic 详情(建议用 zap/slog)
log.Printf("PANIC %s %s: %+v", method, path, r)
// 强制写响应:c.SendStatus() 是安全的,不依赖 ctx 内部状态
c.Status(status).SendString("Internal Server Error")
}
}()
return c.Next()
}
}
- 不要在 recover 块里调
c.JSON()或c.Send()—— 它们可能触发额外 panic - 避免在 recover 中调
c.Locals()或修改c.Response(),这些操作在 panic 状态下不稳定 - 生产环境务必关闭
fiber.Config{DisableStartupMessage: true},防止 panic 日志混入启动 banner
业务 error 怎么统一转成 JSON 响应
Fiber 不区分“业务错误”和“系统错误”,所有 error 都得你自己决定是否返回、怎么返回。推荐在路由 handler 里用 if err != nil 显式判断,而不是依赖某个全局钩子。
- 定义标准错误结构:
type ErrorResponse struct { Code int `json:"code"` Message string `json:"message"` } - 封装响应函数:
func SendError(c *fiber.Ctx, status int, msg string) error { return c.Status(status).JSON(ErrorResponse{Code: status, Message: msg}) } - 在 handler 里直接用:
if err := db.QueryRow(...); err != nil { return SendError(c, 500, "db failed") } - 别试图把 error 转成 HTTP 状态码自动映射(比如
ValidationError → 400),Fiber 不提供这种反射能力,硬做只会让错误路径不可控
常见踩坑:c.Next() 后还继续执行导致状态码被覆盖
这是最隐蔽的错误:你在中间件里写了 c.Status(401).SendString("unauthorized"),但忘了 return,接着又执行了 c.Next(),后续 handler 又调了 c.JSON() —— 最终客户端收到的是 200,不是 401。
- 每次手动设状态后,必须
return,或用return c.Status(...).Send(...)链式返回 error - 权限中间件典型写法:
if !valid { return c.Status(401).SendString("unauthorized") },后面直接跟return c.Next() - 调试时加
fmt.Printf("status=%d\n", c.Response().StatusCode())确认最终状态,别信中间某次Status()调用
真正的难点不在写 recover,而在于接受 Fiber 的哲学:它不帮你“兜底”,只给你干净的控制权。panic 要自己 recover,error 要自己判断,状态码要自己确认——任何想省掉这几步的写法,都会在压测或异常流量下暴露出来。











