beego.InsertFilter路径匹配需加结尾斜杠(如"/api/")避免前缀歧义;静态资源拦截须用BeforeStatic;方法限制应解耦路径注册;所有中间件必须手动recover防panic。

beego.InsertFilter 路径匹配必须带结尾斜杠
注册中间件时用 beego.InsertFilter("/api", ...) 会意外匹配到 /apixxx 或 /api123,这不是 bug,而是前缀匹配的自然行为。真正安全的做法是显式加结尾斜杠:beego.InsertFilter("/api/", ...),它只匹配以 /api/ 开头的路径(如 /api/v1/users、/api/upload),排除所有歧义。
常见错误现象:中间件在不该触发的地方执行,比如访问 /api-docs 也被拦截;或静态资源路径 /api/static/logo.png 被误判为 API 请求。
- 注册必须在
beego.Run()之前,通常放在main.go的init()或main()开头 - 不要用
"/api/*"—— Beego 不支持通配符语法,*会被当作字面量处理 - 若需精确匹配单个路径(如仅
/api/login),直接写完整路径即可:beego.InsertFilter("/api/login", ...)
静态资源请求必须用 beego.BeforeStatic 注册
默认注册在 beego.BeforeRouter 或 beego.BeforExec 的中间件对 /static/、/public/ 等静态路径完全不生效。这是 Beego 的明确设计:静态文件由独立处理器直读磁盘,绕过整个路由和控制器链。
想拦截私有静态资源(例如用户上传的 PDF),唯一有效入口是 beego.BeforeStatic:
beego.InsertFilter("/static/private/", beego.BeforeStatic, authCheckForStatic)
注意:beego.BeforeStatic 中的 ctx 不含 c.Data、c.TplName 等 controller 特有字段,只能操作 c.Ctx.Request 和 c.Ctx.ResponseWriter。
- 不能依赖
c.GetSession("uid")—— session 尚未初始化,需手动解析 cookie 或 header - 若同时需要 API 权限 + 静态资源权限,得写两套逻辑,分别注册到
BeforeRouter和BeforeStatic - 路径匹配规则与
BeforeRouter一致,也推荐结尾加斜杠:"/static/private/"而非"/static/private"
限制 POST 请求不能只靠中间件内 if 判断
在中间件函数里写 if ctx.Ctx.Request.Method != "POST" { ctx.Next(); return } 看似简单,但容易埋雷:如果该中间件还调用了 ctx.ParseForm() 或读取 ctx.Ctx.Input.RequestBody,那么对 GET 请求也会触发 body 解析,导致后续 controller 无法再读取原始 body(Go 的 http.Request.Body 是一次性流)。
正确做法是把方法限制和路径限制解耦 —— 优先用 InsertFilter 的路径参数缩小作用域,再在中间件内做轻量判断:
- 若只针对某个接口的 POST:
beego.InsertFilter("/api/submit", beego.BeforeExec, postOnlyHandler),然后在postOnlyHandler里快速== "POST"即可 - 避免在中间件里调用任何可能消费
RequestBody的方法,除非你确定只用于 POST/PUT/PATCH - 不用
strings.EqualFold做 method 判断 —— HTTP 方法全大写且固定,==更快更安全
panic 必须在中间件内手动 recover
Beego 不会自动捕获中间件中抛出的 panic。一旦发生,整个请求 goroutine 崩溃,客户端收到空响应或连接中断,日志里也看不到堆栈 —— 这是最隐蔽的线上故障源之一。
所有自定义中间件都应包裹 defer + recover:
func safeMiddleware(ctx *context.Context) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in middleware: %v", r)
ctx.Abort(500, "Internal Server Error")
}
}()
ctx.Next()
}
- 不要只在顶层中间件 recover —— 每个中间件都可能 panic,必须各自防护
-
ctx.Abort()后不会继续执行后续中间件或 controller,适合错误兜底 - 生产环境建议结合 Sentry 或日志系统上报 panic,而不是仅打本地 log


















