Go中处理API错误需统一分类、封装结构化响应并映射HTTP错误:定义APIError结构含code/status/message/details;用中间件统一处理panic和error;按4xx/5xx/业务错误语义化响应;集成validator聚合校验错误到details。

在 Go 中处理 API 调用错误并返回标准化响应,核心是统一错误分类、封装结构化响应体、并在 HTTP 层做一致的错误映射。不建议直接返回原始 error 或 panic,而应将错误转化为可预测的 JSON 响应(如 {"code": 400, "message": "invalid email", "details": {...}})。
定义标准化错误结构
创建一个可序列化的错误响应结构,包含状态码、业务码、用户提示和可选详情:
示例:
type APIError struct {
Code int `json:"code"` // HTTP 状态码(如 404)
Status string `json:"status"` // 如 "Not Found"
Message string `json:"message"` // 用户友好的提示
Details interface{} `json:"details,omitempty"`
}
func (e *APIError) Error() string { return e.Message }
// 快捷构造函数
func NewBadRequest(msg string, details ...interface{}) *APIError {
err := &APIError{
Code: http.StatusBadRequest,
Status: http.StatusText(http.StatusBadRequest),
Message: msg,
}
if len(details) > 0 && details[0] != nil {
err.Details = details[0]
}
return err
}
统一错误中间件或包装器
避免每个 handler 重复写 json.NewEncoder(w).Encode(...)。推荐使用中间件捕获 panic 和显式错误:
立即学习“go语言免费学习笔记(深入)”;
- 用
http.Handler包装器,在 defer 中 recover panic,并转为500 Internal Server Error - 约定 handler 返回
error(如func(w http.ResponseWriter, r *http.Request) error),由包装器统一处理 - 若返回非 nil error,检查是否为
*APIError;是则按其 Code/Message 渲染;否则视为未预期错误,记录日志并返回 500
区分错误来源并映射到语义化响应
不同错误应有明确归因,便于前端处理和监控:
-
客户端错误(4xx):参数校验失败(
validator库)、缺失 header、格式错误 —— 用NewBadRequest或NewUnauthorized - 服务端错误(5xx):数据库超时、下游 API 不可用、空指针 panic —— 记录完整 error 栈,返回泛化提示(如 "service unavailable"),避免泄露内部信息
-
业务逻辑错误(常复用 4xx):用户不存在、权限不足、余额不足 —— 使用专属业务码(如
"user_not_found")放入Details字段,方便前端分支处理
集成校验与错误收集
对请求体(如 json)做结构化校验时,不要只返回第一个错误。用 github.com/go-playground/validator/v10 收集全部字段错误,再聚合进 APIError.Details:
type CreateUserRequest struct {
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"required,gte=0,lte=120"`
}
// 校验后
if err := validate.Struct(req); err != nil {
errs := make(map[string]string)
for _, e := range err.(validator.ValidationErrors) {
errs[e.Field()] = e.Tag() + " validation failed"
}
return NewBadRequest("validation failed", errs)
}










