错误处理
错误流
service 返回 error,由 handler 统一转成 response.Fail;全局异常兜底在 pkg/middleware。
service return err
→ handler _ = c.Error(...) + return
→ middleware.Recover 渲染(HTTP 状态恒 200,body 里 code/Msg)三条铁律
- 错误一律
_ = c.Error(...)后return,绝不自己c.JSON错误响应——统一由middleware.Recover渲染。 - 只翻译 service 导出的 哨兵错误(
errors.Is),其余原样c.Error(err)兜底。 - 出参只用
response.Ok(...)/response.OkVoid(), 不写gin.H。
错误类型:errs.ServiceError
go
type ServiceError struct {
Code int // 业务码;0 表示未指定,Recover 回落 500
Msg string // 回前端的文案
Detail string // 只进服务端日志,不回前端
}构造:errs.New(code, msg, detail)。Detail 放 err.Error() 这类内部细节, 只进日志不回前端。
错误码选用
响应体字段 code( HTTP 状态恒为 200,下面列的是 body 里的 code 值,不是 HTTP 状态码):
| 场景 | 构造 | 响应体 code |
|---|---|---|
| 参数格式 / 缺失 | errs.New(response.CodeBadRequest, "参数校验失败", err.Error()) | 400 |
| 业务失败(重复、冲突) | errs.New(response.CodeFail, "xx失败,yy已存在", "") | 500 |
| 资源不存在 | errs.New(response.CodeNotFound, "Xxx 不存在", "") | 404 |
| service 内自定义文案 | errs.New(0, "内置参数【k】不能删除", "") | 500 + 该文案 |
第三个参数 Detail 只进日志不回前端。Code=0 时 middleware.Recover 回落到 CodeFail(500);传非零 Code 时按 response.FailCode 渲染。
哨兵错误翻译示例
go
if err := systemservice.XxxSvcApp.UpdateXxx(c.Request.Context(), &b); err != nil {
if errors.Is(err, systemservice.ErrXxxKeyExists) {
_ = c.Error(errs.New(response.CodeFail,
fmt.Sprintf("修改'%s'失败,key已存在", b.Name), ""))
return
}
if errors.Is(err, systemservice.ErrXxxNotFound) {
_ = c.Error(errs.New(response.CodeNotFound, "Xxx 不存在", ""))
return
}
_ = c.Error(err) // 兜底
return
}
c.JSON(http.StatusOK, response.OkVoid())详见 pkg 参考 / errs。