BeforeRouter 是应用启动时执行一次的初始化函数,无请求上下文,不能用于权限校验;鉴权应使用 BeforeExec 中间件或 Controller 的 Prepare 方法。

Beego 的 BeforeRouter 拦截器不是全局中间件,它只在路由匹配前执行,且无法修改请求路径或跳过后续路由匹配 —— 这点和 Prepare 或自定义中间件有本质区别。
BeforeRouter 是什么,为什么不能当权限校验用
BeforeRouter 是 Beego 应用启动时注册的函数,它在任何路由解析前被调用一次,仅执行一次,不接收 *context.Context,也不参与每次请求生命周期。它常用于初始化全局状态(如加载配置、注册自定义函数),但无法对单个 HTTP 请求做拦截或响应控制。
- 常见误用现象:
BeforeRouter里写c.Ctx.WriteString或c.Abort—— 会 panic,因为此时c根本不存在 - 它没有
Controller实例,只有*beego.App可用,参数签名是func(*beego.App) - 如果你需要“每次请求都先检查 token”,这不是
BeforeRouter的职责,该用Prepare或全局中间件
真正能拦截每个请求的替代方案:Prepare 方法
控制器级别的统一前置逻辑,应直接重写 Prepare 方法。它在每次请求进入该 Controller 时自动调用,可访问完整上下文。
-
Prepare中可安全调用c.Abort(401)、c.Ctx.Input.SetData、c.Ctx.Request.Header.Get("Authorization") - 若所有控制器都要统一处理,建议定义一个基类(如
BaseController),让其他控制器嵌入它 - 注意:Beego 不会自动调用父类
Prepare,子类必须显式调用super.Prepare()(即c.Controller.Prepare())
func (c *BaseController) Prepare() {
token := c.Ctx.Request.Header.Get("Authorization")
if !isValidToken(token) {
c.Abort("401")
return
}
c.Data["user_id"] = getUserIdFromToken(token)
}
需要跨 Controller 的全局拦截?用 beego.InsertFilter
这是 Beego 提供的真正意义上的中间件注册方式,支持 BeforeRouter、BeforeExec、AfterExec 等多个钩子点 —— 但注意:beego.BeforRouter(拼写带 o)是旧版 typo,新版必须用 beego.BeforeRouter(无 o),否则静默失效。
- 推荐使用
beego.BeforeExec:在路由匹配成功、Controller 执行前,此时已有*context.Context,可读写请求/响应 - 注册位置必须在
main()函数中beego.Run()之前,且不能晚于路由注册 - 容易踩的坑:过滤器函数返回后,若未调用
ctx.Abort(),流程继续;若调用了,后续过滤器和 Controller 都不会执行
func authFilter(ctx *context.Context) {
if strings.HasPrefix(ctx.Request.URL.Path, "/api/") {
auth := ctx.Request.Header.Get("Authorization")
if !checkJWT(auth) {
ctx.ResponseWriter.WriteHeader(401)
ctx.ResponseWriter.Write([]byte(`{"error":"unauthorized"}`))
ctx.Abort()
}
}
}
func main() {
beego.InsertFilter("/*", beego.BeforeExec, authFilter)
beego.Run()
}
别混淆:BeforeRouter vs BeforeExec vs Prepare
三者触发时机和能力完全不同:
-
BeforeRouter:应用启动时执行一次,无请求上下文,仅用于初始化 -
BeforeExec:每次请求、路由匹配成功后、Controller 方法执行前,有完整*context.Context,适合通用鉴权/日志 -
Prepare:每个 Controller 实例方法执行前,有*Controller实例,适合控制器内定制逻辑(如统一设置模板变量)
最易被忽略的是:很多开发者把 BeforeRouter 当成“第一个中间件”去写鉴权逻辑,结果发现完全不生效 —— 因为它压根不参与请求处理流。真要拦截请求,从 BeforeExec 开始看,别绕弯。


















