beego错误处理需协同三层:this.abort主动中止请求并跳转错误页,defer recover()捕获控制器内panic并记录日志,errorhandler统一响应http状态码与自定义错误名;三者缺一不可、职责分明。

Beego 的错误处理不是靠“写一个全局函数”就能一劳永逸的,它分三层:请求中止(this.Abort)、panic 恢复(recover 在 goroutine 内)、HTTP 状态码兜底(ErrorHandler)。混用或漏掉其中一层,就容易出现 500 页面不显示、日志没堆栈、自定义错误页失效等问题。
用 this.Abort 主动中止请求并跳转到错误页
这是最常用、也最容易被误用的方式。它不抛 panic,只是立即终止当前控制器方法执行,并触发 Beego 内置的错误页路由逻辑。
-
this.Abort("404")会跳转到你注册的 404 处理函数,或默认 404 页面;this.Abort("dbError")则走你用beego.ErrorHandler("dbError", dbError)注册的自定义处理函数 - 必须在控制器方法内调用,且调用后后续代码**完全不执行**——这点常被忽略,比如在
if err != nil后面忘了 return,导致this.Abort和后续逻辑同时跑 - 字符串参数必须提前注册,否则 Beego 会 fallback 到 500 页面,且不报错、不打日志,排查困难
- 不能用于捕获 panic,它只做流程控制,和
recover无关
在每个 Controller 方法里加 defer recover() 捕获 panic
Beego v2 默认开启 recoverpanic = true,但它只是返回空白 500 页面,不记录堆栈。真要日志+告警,必须自己在每个 Get/Post 方法开头手动加 defer。
- 别在
main()函数里 defer —— main 的 goroutine 捕不到 HTTP 请求里的 panic - 正确写法是:
func (c *MainController) Get() { defer func() { if r := recover(); r != nil { beego.Error("panic recovered:", r, string(debug.Stack())) c.Ctx.ResponseWriter.WriteHeader(500) c.Ctx.WriteString("Internal Server Error") } }() // 你的业务逻辑 } - 如果用了 ORM 或第三方库,它们内部 panic 很常见(比如数据库连接断开时
o.Read()panic),不加这层 defer 就直接崩掉整个请求
用 beego.ErrorHandler 统一接管标准状态码和自定义错误名
这是兜底层,决定用户最终看到什么页面。它不处理 panic,也不影响控制器流程,只响应 this.Abort 或未捕获 panic 后的 HTTP 状态码。
- 注册必须在
beego.Run()之前,且不能重复注册同一 key,后注册的会覆盖前一个 - 支持两种 key:
"404"这类标准状态码字符串,和"dbError"这类任意自定义名;但自定义名必须和this.Abort("dbError")中的字符串**完全一致**(区分大小写) - 处理函数签名固定为:
func(http.ResponseWriter, *http.Request),不能用*context.Context,也不能访问this.Data或 session - 模板路径要写全:
beego.BConfig.WebConfig.ViewsPath + "/404.html",否则ParseFiles找不到文件,静默失败
别依赖 InsertFilter 做全局 panic 捕获
网上很多教程教你在 beego.InsertFilter("*", beego.BeforeRouter, recoverMiddleware) 里 recover,这在 Beego v2 中基本无效。
-
BeforeRouter阶段还没进入控制器,panic 还没发生;等真正 panic 时,filter 已退出,recover失效 - Beego 的 filter 是按阶段执行的,
recover必须在 panic 发生的同一 goroutine、同一调用栈中执行,filter 的生命周期不满足这个条件 - 实测中,这种写法对 controller 内 panic 完全无感,只会捕获路由匹配前的 panic(比如配置解析失败),覆盖场景极窄
真正统一的关键在于:把 this.Abort 当成主动错误出口,把每个 controller 方法内的 defer recover() 当成 panic 安全网,再用 beego.ErrorHandler 把这两条路径最终都导向一致的响应格式和页面。三者缺一不可,且各自职责不能越界。











