Echo默认不处理panic,因设计上认为panic属编程错误,需显式启用middleware.Recover()并配置HTTPErrorHandler返回JSON错误,且goroutine内panic无法被中间件捕获。

为什么Echo默认不处理panic
Echo框架本身不会自动recover任何panic——它把控制权完全交给你。你写一个panic("oops")在handler里,服务直接502或连接中断,日志里只有一行runtime stack trace,HTTP响应体为空。这不是bug,是设计:Echo认为panic属于编程错误,不该由框架兜底掩盖。
必须显式启用middleware.Recover()
要让Echo捕获panic,得主动调用e.Use(middleware.Recover()),且这行代码必须在注册所有路由之前执行。否则中间件链压根不生效。
-
middleware.Recover()只是注册了defer+recover逻辑,它本身不决定返回什么内容 - 它默认只打印log,不写HTTP响应;如果你没配
e.HTTPErrorHandler,panic后客户端收到的仍是空响应或超时 - 别把它和Gin的
gin.Recovery()混淆——Echo的recover中间件更“裸”,需要你补全错误渲染逻辑
如何让recover返回标准JSON错误而不是空白响应
单纯e.Use(middleware.Recover())不够。你需要覆盖e.HTTPErrorHandler,让它在panic被recover后接管错误输出:
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
e.HTTPErrorHandler = func(err error, c echo.Context) {
// 这里的err是recover()拿到的panic值,类型是interface{}
// 注意:c.Response().Committed()为true时不能再写body
if !c.Response().Committed() {
c.Logger().Error(err)
c.JSON(http.StatusInternalServerError, map[string]string{
"error": "Internal server error",
})
}
}
- panic值传入
HTTPErrorHandler时已转为error类型(通过fmt.Errorf("%v", r)包装),但原始类型信息丢失 - 若需区分panic类型(比如
panic(&HTTPError{Code: 404})),得在recover中间件里自己做类型断言,再塞进context或error chain - 别忘了检查
c.Response().Committed(),否则可能触发http: multiple response.WriteHeader callspanic
goroutine里的panic永远捕获不到
这是最常被忽略的硬限制:middleware.Recover()只对当前HTTP handler goroutine有效。你在handler里起一个go func() { panic("in goroutine") }(),这个panic永远不会被中间件捕获,进程直接崩溃。
- 子goroutine必须自己包一层
defer func(){ recover() }() - 如果用
safeGo()封装启动,recover逻辑必须在子goroutine内部,不能靠外层中间件 - echo.Context在子goroutine中不可直接使用——它绑定到原handler goroutine,跨goroutine传递需提取必要字段(如
c.Request().Context())
真正难处理的从来不是handler里的panic,而是那些藏在异步任务、定时器、数据库回调里的panic——它们根本不在Echo的中间件链上,得靠每个goroutine自己守好入口。

















