beego v2 全局 panic 捕获唯一可靠方式是提前设置 beego.bconfig.recoverfunc,需在 beego.run() 前、路由注册后赋值,且必须显式处理 bresp.pageerror 类型以保状态码与响应正确。

Beego v2 的全局 panic 捕获不能靠中间件实现,因为它的中间件不提供 Next() 机制,defer recover() 放在中间件里根本不会生效。唯一可靠、官方支持的入口是替换 BConfig.RecoverFunc。
beego.BConfig.RecoverFunc 是唯一可控入口
Beego v2 启动时会在每个请求 goroutine 中自动调用 RecoverFunc 来捕获 panic,这个函数由 BConfig.RecoverFunc 指定,默认是内部的 defaultRecoverPanic。它不对外暴露,也不能直接覆盖——必须在框架初始化早期(beego.Run() 之前)完成赋值。
-
RecoverFunc类型签名固定为func(*context.Context, *web.Config),不可更改 - 必须在
main()函数中、调用beego.Run()前设置,晚了会被框架覆盖 - 不能在中间件或控制器里改,那只是局部变量,不影响全局行为
- 若你同时依赖默认逻辑(比如处理
bresp.PageError),建议先保存原函数再包装调用
自定义 RecoverFunc 必须处理 PageError 类型
Beego 内部用 bresp.PageError 表达业务级错误(如 this.Abort("404") 或 panic(&bresp.PageError{Code: 401})),你的 RecoverFunc 若忽略它,会导致状态码丢失、返回空白页或 500。
- 务必用
if e, ok := err.(bresp.PageError); ok显式判断 -
e.Code == 200时走 JSON 响应(ctx.JSONResp()),否则用ctx.Output.SetStatus(e.Code)+ 原始字符串 - 未识别的 panic(非
PageError)建议 fallback 到原RecoverFunc,避免破坏日志/堆栈逻辑 - 别在
RecoverFunc里写复杂日志或发告警——那是控制器层该干的事;这里只做“兜底响应”
main.go 中注册 RecoverFunc 的正确顺序
顺序错一步,整个自定义就失效。常见错误是把设置语句放在 beego.Router() 之后,或误写成 beego.BConfig.WebConfig.RecoverFunc(路径错误)。
- 路径必须是
beego.BConfig.RecoverFunc,不是WebConfig下的子字段 - 必须在
beego.Run()前,且最好在所有路由注册之后、运行之前 - 示例代码位置(
main.go):
func main() {
// 路由注册
beego.Router("/", &controllers.MainController{})
// ⚠️ 关键:在这里设置 RecoverFunc
old := beego.BConfig.RecoverFunc
beego.BConfig.RecoverFunc = func(ctx *context.Context, cfg *web.Config) {
if err := recover(); err != nil {
if e, ok := err.(bresp.PageError); ok {
ctx.Output.SetStatus(e.Code)
if e.Code == 200 {
ctx.JSONResp(bresp.RespMessage(e.Message))
} else {
ctx.WriteString(e.Message)
}
return
}
// fallback 到默认逻辑
if old != nil {
old(ctx, cfg)
}
}
}
beego.Run()
}
注意:别在 RecoverFunc 里 panic,也别调用 this.Abort() ——此时 this 不存在,ctx 是唯一可用对象。
真正容易被忽略的是:RecoverFunc 不处理 this.Abort() 触发的流程,它只捕获 panic;而 PageError 是 Beego 内部把 this.Abort() 转成 panic 的结果。所以你的业务代码想走统一异常流,得主动 panic(&bresp.PageError{...}),而不是只靠 Abort。











