




HTTP状态码应由handler显式控制,中间件无法在响应体写出后修改;必须在Write前设置,推荐使用net/http中的http.StatusOK等常量,避免硬编码数字。
Go 的 http.ResponseWriter 状态码默认是

Write 或 WriteString),状态码就无法再修改。这意味着你不能在 handler 执行完之后、响应已写出的情况下靠中间件“补救”状态码。
常见错误是:中间件想统一处理错误并设成 500,但 handler 已经写了响应,此时调用 w.WriteHeader(500) 完全无效,客户端仍收到 200。
ResponseWriter 包装)defer 在 handler 结尾统一设状态码——万一 panic 或提前 return,逻辑就漏了Go 没有强制规范,但 HTTP 语义必须对齐 RFC 7231。实际开发中容易混淆的是 400 和 422、401 和 403、500 和 502。
400 Bad Request:请求语法错误,比如 JSON 解析失败、URL 参数缺失必填字段(id= 但没值)422 Unprocessable Entity:语法正确但语义非法,比如 JSON 字段类型错("age": "abc")、业务规则校验失败(余额不足)401 Unauthorized:缺 token 或 token 过期,需要重新认证;403 Forbidden:token 有效但权限不足(如普通用户访问管理员接口)500 Internal Server Error:服务端 panic、DB 连接崩、空指针——未知错误兜底;502 Bad Gateway 仅用于反向代理场景(如 gin 前面挂 nginx 转发到 Go 服务,Go 挂了才返回 502)直接写 w.WriteHeader(404) 可读性差,也容易打错。标准库 net/http 提供了全部常量,应该直接用。
立即学习“go语言免费学习笔记(深入)”;
import "net/http"
func handler(w http.ResponseWriter, r *http.Request) {
if id := r.URL.Query().Get("id"); id == "" {
w.WriteHeader(http.StatusNotFound) // ✅
// w.WriteHeader(404) // ❌ 不推荐
w.Write([]byte(`{"error": "id required"}`))
return
}
}
http.StatusXXX 形式存在,IDE 可自动补全,也方便 grep 统计很多团队习惯所有接口都返回 {"code": 0, "msg": "", "data": {}} 结构,然后永远用 200。这破坏了 HTTP 语义,让 Nginx 日志、Prometheus 监控、浏览器 DevTools 失去状态判断能力。
http.StatusOK,错误按语义用对应 4xx/5xx,body 里可以保留 code 字段作业务码(如 "code": 1002 表示“库存不足”),但 HTTP 状态码必须真实反映交互结果res.code !== 0,极易遗漏状态码不是装饰,是协议契约。错用一次,排查链路就多绕三步。