不能用 defer 做路由后置操作,因为 defer 在 handler 返回时即执行,无法感知响应是否真正发出、是否发生 panic 或被中间件拦截;应使用 app.use() 在所有路由后注册中间件,或通过 errorhandler 统一处理错误后置。

为什么不能用 defer 做路由后置操作?
很多人第一反应是写 defer,但 Fiber 的请求生命周期里,defer 在 handler 函数返回时就执行了——而此时响应可能还没真正写出(比如用了流式响应、压缩中间件、或自定义 ResponseWriter),更关键的是:它无法感知「响应是否已发送」或「是否发生 panic 导致写入失败」。defer 只管函数退出,不管 HTTP 状态是否已落网关或客户端。真实线上环境里,你常会发现日志里记录了「处理完成」,但监控显示响应耗时异常高,或者错误率突增——问题往往出在你以为的「后置」其实没等响应真正发出。
app.Use() 链末尾加中间件是最稳的后置入口
Fiber 没有类似 Flask 的 after_request,但它的中间件链天然支持「响应发出后」的钩子位置:把你的后置逻辑放在所有路由注册之后、用 app.Use() 注册一个全局中间件,它会在所有 next(c) 返回后执行,也就是路由 handler 已结束、响应头/体已写入 c.Response() 之后。
注意三点:
- 必须放在 所有
app.Get()/app.Post()之后,否则会被提前跳过 - 中间件里要调用
next(c),且不能 return,否则中断链路 - 读取状态码要用
c.Response().StatusCode,而不是c.Status()(后者是设置用的)
app := fiber.New()
// ... 其他中间件和路由
app.Get("/api/user", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"id": 123})
})
// ✅ 后置操作放在这里:所有路由之后
app.Use(func(c *fiber.Ctx) error {
// 此时路由 handler 已执行完,响应已开始写入
statusCode := c.Response().StatusCode()
path := c.Path()
duration := time.Since(c.Context().StartTime()).Milliseconds()
log.Printf("POST %s %d %dms", path, statusCode, int64(duration))
return c.Next() // 必须调用,且不 return 错误(否则拦截响应)
})
需要修改响应体?别碰 after_request 思维,用 ctx.Response().Body() + Reset()
Fiber 不允许在响应写出后再修改 body,所以「后置修改响应」不是靠拦截,而是靠重写:在后置中间件里读取已生成的 body,处理后再重新写入。但要注意——这会吃内存、影响流式场景,只适合小响应体。
典型做法:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 用
c.Response().Body()获取原始字节 - 做你需要的转换(如加签名、脱敏、注入字段)
- 调用
c.Response().Reset()清空当前响应 - 用
c.Status().SendBytes()重发新内容
⚠️ 风险:如果上游中间件已调用 c.SendStream() 或设置了 Transfer-Encoding: chunked,这个方式会失效。生产环境建议只用于 JSON API 类型接口,并加 Content-Type 判断。
真正需要「错误后置」或「panic 捕获后置」?得靠 app.ErrorHandler
路由 handler 中 panic 或显式调用 c.Status(500).SendString(),不会走上面那个 Use() 中间件——它只对正常返回的 handler 生效。要统一捕获异常后的清理或上报,必须替换默认错误处理器:
app := fiber.New(fiber.Config{
ErrorHandler: func(c *fiber.Ctx, err error) error {
// 这里能拿到原始 err 和上下文
log.Printf("Panic or error on %s: %v", c.Path(), err)
// 执行清理:关闭 DB 连接、释放临时文件、发告警
cleanupResources(c)
// 最后仍需返回标准错误响应
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "Internal server error",
})
},
})
这个 ErrorHandler 是 Fiber 中唯一能稳定覆盖所有错误路径的后置点,比任何中间件都底层。别试图在普通中间件里 recover —— Fiber 的 panic 捕获机制绕过了中间件链。
最易被忽略的一点:Fiber 的「后置」不是时间上的「最后一步」,而是语义上的「责任终点」。你写的每一段后置代码,都该明确回答一个问题:它依赖的是「handler 执行完毕」,还是「响应已抵达客户端」?前者用 Use(),后者目前 Fiber 无原生支持,得靠客户端 ACK 或日志+监控交叉验证。










