Beego v2 中间件必须用 func(http.Handler) http.Handler 签名,通过 web.InsertFilter() 在 web.Router() 前注册,路径匹配仅支持前缀(如 "/*"),且需用 web.NewContext 包装请求才能安全访问 Beego 上下文。

Beego 项目里加中间件,不是往 controllers 或 routers 里随便塞个函数就完事的;它必须在路由注册前注入、要区分全局和局部作用域、且 v2 版本的中间件签名和 v1 不兼容——用错签名或注册时机,bee run 能跑起来,但请求根本进不去中间件。
中间件函数签名必须匹配 web.MiddleWareFunc
v2 的中间件类型是 web.MiddleWareFunc,签名固定为:func(http.Handler) http.Handler。写成 func(*web.Context) 或 func(web.Controller) 都会静默失效——没有编译报错,但 c.Ctx.Input.IsAjax() 这类调用永远拿不到预期值。
- 正确写法(返回包装后的
http.Handler):func AuthMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("Authorization") if token == "" { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } next.ServeHTTP(w, r) }) } - 错误写法(看似能编译,但不会被 Beego 调用):
func AuthMiddleware(c *web.Context)或func AuthMiddleware() bool - v1 用户迁移时尤其注意:v1 的
beego.InsertFilter接收的是func(*context.Context),v2 必须重写为标准http.Handler链式结构
InsertFilter 的注册位置和顺序决定是否生效
中间件必须在 web.Router 之前注册,且顺序影响执行链。放在 routers/router.go 的 init() 函数末尾、或 main.go 的 web.Run() 之前都行,但绝不能在某个 controller 的 Init() 里注册。
- 推荐位置:在
routers/router.go的init()开头处,紧挨着import块之后:func init() { web.InsertFilter("/*", web.BeeAppHandler, AuthMiddleware, true) web.Router("/", &controllers.MainController{}) } -
"/*"表示全局匹配;"/api/*"只对 API 路由生效;路径不支持正则,只认前缀 - 第四个参数
true表示“是否在 Router 之前执行”,设为false会导致中间件在路由匹配后才触发,此时c.Ctx.Input.Param(":id")还没解析出来
中间件里访问 Beego 上下文要用 web.NewContext
原生 http.Handler 拿不到 c.Data、c.TplName 这些 Beego 特有字段。想在中间件里读取 Session、写日志、或提前终止请求并返回 JSON,得把 *http.Request 包装成 Beego 的 *web.Context。
- 手动构造(仅限必要场景):
ctx := web.NewContext(&web.Context{Ctx: context.WithValue(r.Context(), web.ContextKey, nil)}) ctx.Input.SetData("from_middleware", "auth_ok") - 更稳妥的做法:把鉴权逻辑下沉到 controller 的
Prepare()方法里,用c.Abort("401")统一拦截——中间件只做无状态校验(如 JWT 解析),状态相关操作交给 controller - 不要在中间件里调用
c.ServeJSON()或c.Redirect(),这些方法依赖完整的 controller 生命周期,此时c.TplName为空会导致 panic
最易被忽略的一点:Beego v2 的中间件无法直接修改 c.Data 并透传给后续 controller,因为 web.Context 是每次请求新建的。如果需要跨中间件+controller 传递数据,必须用 r.Context().WithValue() + r.Context().Value(),而不是往 c.Data 写。


















