Buffalo路由钩子本质是需显式调用c.Next()的中间件,否则请求静默终止;全局注册于App.Use(),用App.Middleware.Skip()排除路径;RequestFinished钩子确保请求结束时执行清理,不受中间件链影响。

Buffalo 路由钩子不是自动注册的,必须显式调用 c.Next()
Buffalo 的“路由钩子”本质是中间件(middleware),它不会像 Express 或 Gin 那样在匹配路由后自动串行执行所有中间件。如果你写了中间件函数但没在 handler 里调用 c.Next(),那后续 handler 就根本不会运行——请求会在这里静默结束,返回空响应或 404(取决于是否匹配到其他路由)。
常见错误现象:
- 加了日志中间件,但日志没输出,HTTP 响应码却是 200
- 加了鉴权中间件,但未授权用户仍能访问受保护接口
- 中间件里修改了
c.Request().Context()或c.Data,但下游 handler 拿不到变更
正确写法要点:
- 中间件函数签名必须是
func(next buffalo.Handler) buffalo.Handler - 在中间件内部必须显式调用
next(c)(即c.Next())才能进入下一个环节 - 若想提前终止流程(如鉴权失败),就不要调用
next(c),直接写c.Render(401, ...)或c.Error(...) - 中间件顺序很重要:越靠前的,越早拿到请求;越靠后的,越接近最终 handler
App.Use() 和 App.Middleware.Skip() 控制作用范围
Buffalo 不支持 per-route middleware 注册(比如 r.Get("/admin", h, auth) 这种写法不存在)。所有中间件都通过 App.Use(mw) 全局注册,然后靠 App.Middleware.Skip() 显式排除不需要的路由。
典型使用场景:
- 全局加 CORS、日志、panic 恢复中间件 → 放在
App.Use()最前面 - 只对 API 路由加 JWT 解析 → 先
App.Use(jwtMW),再App.Middleware.Skip(jwtMW, "/login", "/healthz") - 静态文件服务(
App.Static(...))默认跳过所有中间件,无需手动 skip
注意:App.Middleware.Skip() 的第二个参数是路由路径前缀(string slice),不是正则也不是通配符;不支持 /api/* 这种写法,得列全 "/api/users", "/api/posts" 或改用条件判断逻辑。
生命周期扩展别依赖中间件顺序,用 App.Hooks() 更可靠
如果想在“整个请求生命周期结束前”统一做清理(比如关闭临时文件、释放 DB 连接池资源、上报 trace),中间件链不是好选择——因为中间件执行顺序受 Use() 和 Skip() 影响,且无法保证一定在 panic 后执行。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
Buffalo 提供了更底层的钩子机制:App.Hooks().BeforeStart()、App.Hooks().AfterStop(),但最常用的是 App.Hooks().RequestFinished():
-
RequestFinished()在每次 HTTP 请求完全处理完毕(无论成功/失败/panic)后触发,参数是*buffalo.Request和*buffalo.Response - 它不参与中间件链,不受
Skip()影响,也不会阻塞主流程 - 适合做 metrics 上报、审计日志、资源回收等“副作用”操作
示例:
app.Hooks().RequestFinished(func(c buffalo.Context, res http.ResponseWriter) {
log.Printf("req=%s status=%d duration=%v", c.Request().RequestURI, res.Status(), time.Since(c.Time()))
})
自定义中间件里别直接操作 c.Session() 或 c.Cookies() 而不检查错误
Buffalo 的 session 和 cookie 操作都是带 error 返回的,但很多人习惯性忽略。比如 c.Session().Set("user_id", 123) 看似无害,但如果 session store 初始化失败(如 Redis 连不上),这个 set 实际没生效,下游却以为已存入。
容易踩的坑:
- 在中间件中调用
c.Session().Get()返回nil,但没检查是否因 store 错误导致读取失败 - 调用
c.Cookies().Set()时传了非法 domain 或过期时间,导致浏览器拒收 cookie,但代码没报错 - 在
RequestFinished钩子里尝试读c.Session(),但此时 session 已被自动保存并清空,读出来总是空
建议做法:
- 所有
c.Session().*和c.Cookies().*调用后,用if err != nil { log.Error(err) }至少打日志 - 敏感操作(如登录态写入)后,用
c.Session().Get()回读验证一次 - 需要在生命周期末尾访问 session 数据,应在中间件中提前存到
c.Data["session_data"]里
真正难调试的不是钩子怎么写,而是你不知道它什么时候没被执行——尤其是 panic 发生时,中间件链中断,但 RequestFinished 依然会进。这点常被忽略。


















