必须在 w.write 前调用 w.writeheader 且仅一次;header 修改须在 writeheader 前完成;错误分支后需 return 防止混合响应;responsewriter 按值传递即可。

WriteHeader 调用时机不对,状态码就失效了
Go 的 ResponseWriter 不会强制你先调 WriteHeader,但一旦调用 Write,它就会悄悄帮你补上 WriteHeader(200) —— 此时再想改状态码(比如改成 404 或 500)就晚了,HTTP 头部已发,客户端收到的永远是 200。
常见错误现象:w.WriteHeader(404) 写在 w.Write(...) 后面,结果响应仍是 200;或者压根没调 WriteHeader,却以为自己返回了错误码。
- 必须在
w.Write之前调用w.WriteHeader,且只能调一次 - 如果 handler 中有多个分支(如 if/else),每个分支都要显式设置状态码,不能只靠默认值
-
http.Redirect、http.Error这类辅助函数内部已调用WriteHeader,用完别再手动写
Header 修改必须在 WriteHeader 之前
w.Header().Set("Content-Type", "application/json") 看似无害,但如果这行代码出现在 w.WriteHeader(200) 之后,它就完全无效——不会报错,也不会生效,静默失败。
使用场景:返回 JSON、重定向、流式响应、文件下载等都需要精确控制 header,而 header 的修改窗口只有“WriteHeader 之前”这一段。
- 所有
w.Header()操作(Set、Add、Del)必须在首次调用w.WriteHeader或w.Write前完成 - 注意拼写:
"Content-Type"不是"content-type"(虽然 HTTP header 不区分大小写,但 Go 的Headermap 是大小写敏感的 key) - 某些 status code(如 204、304)不允许带 body,此时即使设置了
Content-Type,w.Write也会触发ErrBodyNotAllowed错误
ResponseWriter 是接口,按值传递就够了
你不需要、也不应该把 http.ResponseWriter 当作指针传,比如写成 func handle(w *http.ResponseWriter, r *http.Request) —— 这不仅多余,还会让调用方困惑,甚至导致编译错误。
原因在于:http.ResponseWriter 本身是接口类型,其底层动态值已经是 *http.response 的指针。按值传递接口,等于传递了一个包含指针的副本,所有方法调用都作用于原始响应对象。
- 标准 handler 签名永远是
func(http.ResponseWriter, *http.Request) - 若需包装或增强
ResponseWriter(如加日志、拦截写入),应定义新结构体并实现该接口,而不是取地址 - 传
*http.ResponseWriter可能让你误以为要解引用才能用,实际根本不需要
Write 后忘记 return,响应变成“混合双打”
这是最隐蔽也最容易复现的 bug:错误处理分支里写了 w.WriteHeader(500) 和 w.Write([]byte("error")),但漏了 return,后续正常逻辑的 w.Write 依然执行 —— 客户端收到的是两段拼接响应,可能解析失败或直接报错。
典型现象:浏览器 Network 面板看到响应体开头是 error message,后面又跟了一串 HTML 或 JSON;curl 抓包发现响应内容重复或错乱。
- 每个错误分支末尾务必加
return,哪怕只是return - 考虑用封装好的 helper 函数统一处理错误,例如
writeJSONError(w, 500, "msg")内部自带return - 静态检查工具(如
revive)可配置规则检测 “missing return after error write”,但无法覆盖所有逻辑路径
WriteHeader 晚了、Header 改晚了、Write 后没 return,Go 都默许你这么做,直到你在生产环境看到奇怪的 200 OK 响应里混着 404 的 body。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











