
Go HTTP handler 里怎么返回带错误码的 JSON
直接用 json.Marshal + http.Error 不行,因为后者强制设 4xx/5xx 状态码且写死 text/plain 类型。正确做法是手动控制响应头和 body。
- 先调用
w.Header().Set("Content-Type", "application/json; charset=utf-8") - 再调用
w.WriteHeader(statusCode)(比如http.StatusBadRequest) - 最后用
json.NewEncoder(w).Encode()写结构体,别用json.Marshal后再w.Write,避免中文乱码或空格问题
定义统一错误响应结构要注意字段名大小写
Go 的 json 包只序列化首字母大写的导出字段。如果定义 errCode int,输出会是 {"errCode":0};但前端通常期望 err_code 或 code 这种下划线风格。
- 用 struct tag 显式指定:
Code int `json:"code"`、Message string `json:"message"` - 别漏掉
http.StatusText(code)自动映射的描述,它和自定义Message是两回事:前者是 HTTP 状态文本(如 "Not Found"),后者是业务提示(如 "用户不存在") - 不要把
data字段设为必须,空数据时传null比空对象更安全
中间件统一拦截 panic 并转成错误码容易漏掉 recover 时机
panic 发生在 handler 执行中,但如果你的 recover 放在 defer 里却没在最外层 handler 包裹,就会被上层框架(比如 Gin 的 c.Next())吃掉,最终返回 500 页面而不是你定义的 JSON 错误。
- 确保 recover 在每个 handler 入口最外层执行,不是在子函数里
- 捕获到 panic 后,仍要调用
w.WriteHeader(http.StatusInternalServerError),否则默认是 200 - 别直接打印 panic 到终端就完事,至少记录
debug.PrintStack()或用log.Printf("%+v", err)
第三方库如 Gin 的 c.AbortWithStatusJSON 为什么有时不生效
这个方法本质是设置状态码 + JSON body + 调用 c.Abort() 阻止后续中间件执行。但它不会自动终止当前 handler 函数 —— 如果你在调用它之后还写了其他逻辑(比如又调了 c.JSON),就会触发 “header already written” panic。
立即学习“go语言免费学习笔记(深入)”;
- 调用
c.AbortWithStatusJSON后必须立即 return,不能继续执行 - 它只对 Gin 的中间件链有效,如果你在 handler 里混用原生
http.ResponseWriter,它完全不起作用 - 状态码必须是 4xx/5xx,传
200会导致前端解析失败(HTTP 状态码和 JSON 中的code字段语义不同)
最麻烦的其实是错误码分层:HTTP 状态码管通信层,自定义 code 字段管业务层,日志里的 trace_id 要能串起来。这三者一旦漏对齐,查问题时就得来回翻代码和 Nginx 日志。










