必须用app.use注册全局中间件,app.middleware仅作用于路由匹配后的handler链,无法拦截404或options请求;注册须在app.new之后、app.serve之前,且顺序靠前才能确保生效。

中间件注册必须用 app.Use,不能用 app.Middleware
Buffalo 的中间件链分两层:app.Use 注册的是全局中间件(所有请求都经过),而 app.Middleware 只作用于匹配到路由后的 handler 链。如果你把鉴权或日志中间件写成 app.Middleware,它根本不会在 404 或预检 OPTIONS 请求中执行——比如你注册了 app.Middleware(auth) 但访问 /nonexistent,auth 就完全不触发。
常见错误是复制其他框架写法,误以为 app.Middleware 是标准入口。实际必须用:
app.Use(func(next buffalo.Handler) buffalo.Handler {
return func(c buffalo.Context) error {
// 中间件逻辑
return next(c)
}
})
这个调用要放在 app = buffalo.New(...) 之后、app.Serve() 之前,且越靠前注册,越早执行。
中间件里怎么安全地读取和修改请求体
Buffalo 默认不缓存 request body,c.Request().Body 是单次可读流。你在中间件里调 c.Request().Body.Read() 一次后,后续 handler 再读就得到空内容——这会导致 c.Bind() 失败、JSON 解析为空、甚至 panic。
正确做法是:只在必要时读,读完后用 bytes.NewReader 重建 Body,并注意限制大小防攻击:
- 先用 http.MaxBytesReader 包装原始 Body,例如限制 10MB:
http.MaxBytesReader(c.Response(), c.Request().Body, 10_485_760) - 用 io.ReadAll 读取全部内容到 []byte
- 检查长度或 JSON 结构(如检测是否含敏感字段)
- 用
c.Request().Body = io.NopCloser(bytes.NewReader(buf))恢复可读性
别跳过大小限制——没这步,恶意上传会直接耗尽内存。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
中间件返回 401 时必须手动设 WWW-Authenticate 头
浏览器发起 Basic 或 Bearer 认证时,如果服务端只返回 401 状态码但没带 WWW-Authenticate 响应头,它不会弹出认证框,而是静默失败。Buffalo 的 c.Error(401, err) 只设状态码和错误体,不设这个头。
要在中间件里正确响应未授权请求,必须显式设置头再渲染:
if !isValidToken(token) {
c.Response().Header().Set("WWW-Authenticate", `Bearer realm="api"`)
return c.Render(401, r.JSON(map[string]string{"error": "invalid or missing token"}))
}
注意 realm 值要和前端请求一致;Bearer 和 Basic 的头值格式不同,别混用。
中间件顺序决定行为是否生效
Buffalo 不校验中间件顺序,但顺序直接影响逻辑是否被绕过。例如版本控制中间件必须放在最前面:
- 错误顺序:app.Use(logging), app.Use(auth), app.Use(versionCheck) → /api/v3/xxx 先进 logging 和 auth,再被 versionCheck 拒绝,浪费资源且暴露日志
- 正确顺序:app.Use(versionCheck), app.Use(auth), app.Use(logging) → 非法版本请求在第一层就被拦截,auth 和 logging 完全不执行
同理,CORS 中间件若放在 auth 后面,OPTIONS 预检可能因未通过鉴权而失败;而它本不该受 auth 约束。真实线上问题常源于这一条:你以为中间件“自动生效”,其实它只是按注册顺序排队执行。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










