beego控制器生命周期为newcontroller→prepare→http方法→finish,prepare必执行且适合鉴权/预处理,finish在响应写出后执行用于清理。

Beego 的控制器不是“一调用就执行 Get/Post 方法”,而是有一整套嵌入式生命周期钩子,Prepare 和 Finish 是最常被忽略但最关键的两个环节。
Prepare 方法在请求处理前自动触发
Prepare 不是可选的初始化函数,而是控制器实例化后、任何 HTTP 方法(Get、Post 等)执行前必经的入口。它天然适合做统一鉴权、参数预处理、上下文注入等前置逻辑。
- 如果在
Prepare中调用c.Abort("401")或c.StopRun(),后续的Get/Post将完全不执行 -
Prepare里访问c.Input.Param是安全的,路由参数已解析完成;但c.Input.RequestBody还未自动解码(需手动调用c.ParseForm()或c.ParseJSON()) - 继承自
beego.Controller的结构体,无需显式重写Prepare—— 基类已有空实现,直接覆盖即可
GetControllerAndAction 返回的是路由匹配结果,不是运行时反射
GetControllerAndAction 返回的 controllerName 和 actionName 来自路由系统解析,不是靠 runtime.FuncForPC 反射当前函数名。这意味着:
- 它在
Prepare阶段就能准确拿到,可用于日志记录或权限路由白名单判断 - 若使用
beego.Any("/path", handler)这类函数式路由,该方法返回空字符串 —— 因为没有绑定控制器结构体 - RESTful 路由中,
beego.Router("/user", &controllers.UserController{}, "get:List;post:Create")会使得GetControllerAndAction在GET /user时返回"UserController"和"List",而非"Get"
Finish 方法只在响应写出后执行,且无法修改输出
Finish 是控制器生命周期最后一个钩子,在 WriteString、TplName、JSON 等响应方法调用并真正写出 HTTP body 后才触发。它不能改变响应内容,但适合做资源清理和异步埋点。
- 不要在
Finish里调用c.Ctx.ResponseWriter.Write—— 此时 header 已发送,会 panic 报错http: WriteHeader called after Body written - 数据库连接池归还、临时文件删除、goroutine 收尾等操作放在这里比放在
Get末尾更可靠(即使中间 panic,Finish仍会被 defer 机制保障执行) - 注意:如果控制器提前调用
c.StopRun(),Finish依然会执行;但如果进程被 kill 或网络中断,它不会被执行
整个生命周期顺序固定为:NewController → Prepare → [Get/Post/…] → Finish。最容易出问题的地方是把本该在 Prepare 做的校验逻辑拖到 Get 开头写,导致重复代码、遗漏分支,或者误以为 GetControllerAndAction 能在任意位置稳定返回 —— 其实它依赖路由阶段已完成,而那个阶段早在 Prepare 之前就结束了。











