应统一用结构化 AppError 包装错误,含 code、message、field 字段,禁用 errors.New;HTTP handler 返回 JSON 对象而非字符串;前端按 code 分支处理,不依赖 HTTP 状态码。

Go 后端怎么把错误传给前端显示
后端不能直接把 error 原样塞进 JSON 响应里发给前端——没结构、没分类、可能带敏感路径或调试信息。得先统一包装成前端能识别的格式,比如带 code、message、field 的对象。
常见错误现象:前端收到 {"error":"invalid email format"},但 UI 不知道该弹框还是内联提示;或者后端 panic 日志被当普通错误返回,暴露了 /var/www/app/main.go:123 这种路径。
- 所有业务错误必须走自定义错误类型,比如
AppError,实现Error()方法且携带Code()和Message() - HTTP handler 中用
json.NewEncoder(w).Encode()返回结构体,不是fmt.Fprintf拼字符串 - 全局 panic 恢复中间件里,对非
AppError类型错误,一律返回通用码如"INTERNAL_ERROR",message 固定为"Something went wrong"
前端怎么区分网络错误、校验失败、权限不足
靠后端返回的 code 字段,不是靠 HTTP 状态码猜。400 不一定代表表单错,500 也不一定真崩了——很多网关会把超时也转成 500。
使用场景:用户提交邮箱,后端返回 {"code":"VALIDATION_FAILED","message":"Email is invalid","field":"email"},前端就只在邮箱输入框下方显示红字;如果是 {"code":"FORBIDDEN"},就跳登录页。
立即学习“go语言免费学习笔记(深入)”;
- 约定好几类标准 code:
VALIDATION_FAILED、NOT_FOUND、FORBIDDEN、RATE_LIMITED,全部大写+下划线 -
field字段只在VALIDATION_FAILED时存在,值对应前端表单控件的name或id - 避免用 HTTP 状态码做业务分支,比如不写
if status === 403跳登录,而应检查res.code === "FORBIDDEN"
为什么不能在 Go 里用 errors.New("xxx") 直接返回
因为它没结构、没上下文、没法加字段,前端拿不到 code,日志里也看不出是哪个接口、哪个参数出的问题。
性能影响不大,但可维护性差:加个新错误要改三处——handler 里写字符串、前端加 case、文档补说明。
- 用结构体定义错误:
type AppError struct { Code string; Message string; Field string; } - 提供构造函数:
NewValidationError(field, msg string) *AppError,内部固定设Code: "VALIDATION_FAILED" - 日志记录时,用
zap.Error(err)打印时会自动展开结构体字段,不用手动拼err.Code + err.Message
UI 层怎么避免重复提示或提示消失太快
不是后端问题,但和错误结构强相关。如果后端每次返回都带完整 message,前端又没做防抖或合并,用户点两次按钮就会弹两个 Toast。
容易踩的坑:把 loading 状态和错误状态耦合,比如请求中禁用按钮,但错误后没恢复按钮状态,用户以为卡住了。
- 前端收到响应后,先清空上一次的
field对应错误,再按新field设置;全局错误(无field)单独存一个 slot - Toast 提示加唯一 key,比如用
code + timestamp,防止相同错误连发被重复渲染 - 表单提交后,按钮置灰逻辑和错误处理解耦:按钮状态由
isSubmitting控制,错误展示由errors对象控制
真正难的是错误边界——比如上传文件时网络中断,后端根本没收到请求,这时候错误不在 AppError 体系里,得靠前端 fetch 的 reject 分支兜底,而这个分支的 message 格式又要和后端保持一致。这事得前后端一起对齐,光后端做得再规范也没用。










