recover仅用于兜底防崩溃,不可拦截业务错误;业务错误须通过error返回值传递,配合自定义apperror结构(含code、statuscode、message、details)和中间件统一渲染,确保可追溯、可分类、前端友好。

为什么不能在 Echo 里用 recover 拦截业务错误
recover 只该用于兜底防崩溃,不是错误处理主干。Echo 中调用 panic()(比如空指针解引用、手动 panic)会中断当前 goroutine,而 recover() 只在 defer 函数中有效,且只能捕获本 goroutine 的 panic —— 一旦用了,原始调用栈就丢了,日志里只剩“panic caught”,查不出哪行代码传了 nil。
业务错误(如参数校验失败、DB 查询无结果、JSON 解析出错)根本不是 panic 场景,必须走 error 返回值路径。框架里把 echo.NewHTTPError(400, "bad request") 包进 return,而不是扔给 recover,才能保留完整上下文和可追踪的 error 链。
如何定义可传播、可分类的自定义错误类型
别只实现 Error() 方法。真实服务需要结构化字段,否则前端不知道该展示什么文案,监控系统没法按码告警,日志也难过滤。
-
Code:整型业务码(如4001表示参数缺失),不用字符串匹配,下游解析稳定 -
StatusCode:对应 HTTP 状态码(400/500),中间件靠它决定响应头 -
Message:面向开发者的调试信息,含请求 ID、输入参数快照,不暴露给用户 -
Details:map 或结构体,存具体失败字段名、期望值/实际值,便于前端高亮报错位置
示例:
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
type AppError struct {
Code int `json:"code"`
StatusCode int `json:"-"`
Message string `json:"message"`
Details map[string]interface{} `json:"details,omitempty"`
}
func (e *AppError) Error() string { return e.Message }
中间件中统一拦截并渲染 error 的正确写法
核心不是“捕获”,而是“检查 + 渲染”。Echo 不提供全局 error handler,但你可以用中间件在 next(c) 后检查返回的 error,再统一转成 JSON 响应。
- 所有 handler 必须返回
error,不要在内部调用c.JSON()或c.String() - 中间件末尾判断
err != nil,区分是否是*echo.HTTPError或自定义*AppError - 对非预期 panic(比如中间件自己 panic),才用 defer + recover 做最后兜底,并转为
&AppError{Code: 50050, StatusCode: 500, Message: "system panic"} - 避免在中间件里直接
c.JSON()后还return err—— Echo 会因响应已写出而 panic
关键逻辑片段:
func ErrorHandler(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
err := next(c)
if err == nil {
return nil
}
var appErr *AppError
if errors.As(err, &appErr) {
return c.JSON(appErr.StatusCode, StandardResponse{
Code: appErr.Code,
Message: appErr.Message,
Data: nil,
Details: appErr.Details,
})
}
// 兜底:未识别的 error 统一转 500
return c.JSON(http.StatusInternalServerError, StandardResponse{
Code: 50000,
Message: "internal server error",
Data: nil,
})
}
}
StandardResponse 结构设计与前后端约定要点
响应结构必须让前端能无脑解析,不能靠状态码猜 data 是否存在,也不能靠 message 内容做逻辑分支。
-
code字段始终是整数,0表示成功,非零表示业务失败(如4001、50051) -
message是纯提示文本,不带堆栈、不带参数,供用户或开发者快速理解问题 -
data字段永远存在,即使为空也设为null或空对象,避免前端res.data?.xxx报错 - 不要把
http.StatusText(code)当作message返回 —— “Internal Server Error” 对用户毫无意义
常见踩坑点:有人把 echo.HTTPError.Message 直接塞进响应的 message 字段,结果返回 "code=404, message=\"Not Found\"" 这种格式化字符串,前端根本没法消费。










