go 的 http.handler 不会自动传播错误,所有错误必须显式处理并写入响应,否则导致客户端卡住、超时或空响应无日志;需检查 w.write() 和 json.encoder.encode() 错误,正确调用 w.writeheader() 且仅一次,避免 panic、log.fatal 或忽略连接中断类错误。

Go 的 http.Handler 不会自动传播错误,所有错误都必须显式处理并写入响应;不这么做会导致客户端卡住、超时,或返回空响应却无日志可查。
HTTP 处理函数内不能 panic 或忽略 error
Go HTTP 服务不会捕获 handler 中的 panic(除非手动加 recover),也不会帮你处理 WriteHeader 或 Write 的底层错误(比如连接已断)。常见表现是日志里没报错,但前端收到空响应或 ERR_EMPTY_RESPONSE。
- 每次调用
w.Write()后应检查返回的error,尤其在流式响应或大文件传输时 - 避免在 handler 中直接调用
log.Fatal或os.Exit,这会杀死整个 server - 不要依赖 defer 关闭 response body——
http.ResponseWriter没有Close方法,它不是io.ReadCloser
正确返回 HTTP 错误状态码与 JSON 错误体
用 w.WriteHeader(statusCode) + json.NewEncoder(w).Encode() 是常见组合,但顺序和错误检查缺一不可。漏掉 WriteHeader 会导致默认 200,掩盖真实问题。
// 正确示例:带错误检查的 JSON 错误响应
func handleUser(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
if id == "" {
w.WriteHeader(http.StatusBadRequest)
err := json.NewEncoder(w).Encode(map[string]string{"error": "missing id"})
if err != nil {
log.Printf("failed to write error response: %v", err)
return
}
return
}
// ...后续逻辑
}
-
w.WriteHeader()必须在任何w.Write()之前调用,且只能调用一次 -
json.Encoder.Encode()可能因连接中断返回write: broken pipe,需检查 - 不要用
fmt.Fprintf(w, "...")返回 JSON——缺少 Content-Type、易格式出错
中间件统一捕获 handler panic 和基础错误
靠每个 handler 自己写 recover 和错误检查太脆弱。推荐用中间件封装公共错误路径,但注意:中间件无法捕获 Write 之后发生的网络错误(如客户端关掉页面)。
动态WEB网站中的PHP和MySQL详细反映实际程序的需求,仔细地探讨外部数据的验证(例如信用卡卡号的格式)、用户登录以及如何使用模板建立网页的标准外观。动态WEB网站中的PHP和MySQL的内容不仅仅是这些。书中还提到如何串联JavaScript与PHP让用户操作时更快、更方便。还有正确处理用户输入错误的方法,让网站看起来更专业。另外还引入大量来自PEAR外挂函数库的强大功能,对常用的、强大的包
立即学习“go语言免费学习笔记(深入)”;
func errorMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 捕获 panic
defer func() {
if rec := recover(); rec != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
log.Printf("panic recovered: %v", rec)
}
}()
next.ServeHTTP(w, r)
})
}
// 使用
http.Handle("/api/", errorMiddleware(http.StripPrefix("/api", apiRouter)))
- 中间件里的
recover()只对当前 goroutine 有效,对子 goroutine 无效 - 不要在中间件里重写
ResponseWriter并忽略其Write返回值——那会掩盖真实写入失败 - 若需记录响应状态码,可用
httputil.NewSingleHostReverseProxy风格 wrapper,但要小心竞态
客户端断连时的 Write 错误如何应对
当用户关闭页面、网络中断或代理超时时,w.Write() 可能返回 write: broken pipe 或 write: connection reset by peer。这类错误通常无需重试或告警,但必须检查,否则可能 panic 或污染日志。
- 对非关键响应(如心跳、埋点上报),可忽略
write错误,但至少打 debug 日志 - 对关键业务响应(如支付结果),应在
Write前设置http.TimeoutHandler,避免长时间阻塞 - 不要把
net.ErrClosed或syscall.EPIPE当成业务错误处理——它们属于传输层,和你的业务逻辑无关
最常被忽略的是:即使你返回了 http.StatusNotFound,如果后续又调用了 w.Write(),Go 会静默忽略状态码并返回 200。一切写操作前,务必确认是否已写过 header,且没有重复写。









