HTTP handler 中不能 return 错误,须手动调用 w.WriteHeader() 和 w.Write() 发送响应;应封装 writeError 工具函数统一处理 JSON 错误格式,并用 recover 中间件兜底 panic,严格区分 4xx 与 5xx 状态码。
Go 的 http.HandlerFunc 返回类型是 void,没有返回值接口,所以写 return err 是语法错误。常见误区是以为能像其他语言那样“抛出异常”或“中断流程”,实际必须手动调用 w.WriteHeader() + w.Write() 才算完成响应。
正确做法是:在出错时立即写入状态码和响应体,并避免后续逻辑继续执行(比如不要在 if err != nil 后面还调用 json.NewEncoder(w).Encode(...))。
http.StatusBadRequest、http.StatusNotFound、http.StatusInternalServerError
error 字段,例如:{"error": "invalid ID format"}
return,防止后续代码意外写入已关闭的 ResponseWriter重复写 w.WriteHeader() + json.Marshal() + w.Write() 容易遗漏或不一致。推荐封装一个 writeError(w http.ResponseWriter, status int, msg string) 函数,集中控制格式和 Content-Type。
注意点:
w.Header().Set("Content-Type", "application/json; charset=utf-8"),否则前端可能解析失败w.Write() 前调用 w.WriteHeader(status);如果已经写过响应体,再调用会静默忽略func writeError(w http.ResponseWriter, status int, msg string) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(status) json.NewEncoder(w).Encode(map[string]string{"error": msg}) }
未捕获的 panic 会导致连接被关闭,客户端收不到任何响应。生产环境必须用中间件兜底:
defer/recover 捕获 handler 内 panicw.WriteHeader(http.StatusInternalServerError),否则默认是 200func recoverMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("PANIC in %s %s: %v", r.Method, r.URL.Path, err)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{"error": "internal server error"})
}
}()
next.ServeHTTP(w, r)
})
}
4xx 错误(如 http.StatusBadRequest、http.StatusUnauthorized)表示请求本身有问题,应由客户端修正;5xx 表示服务内部故障,需要后端排查。混用会导致监控失真、重试策略失效。
典型场景判断:
http.StatusBadRequest
http.StatusInternalServerError
http.StatusUnauthorized(不是 403)http.StatusNotFound,而非 500最常被忽略的是:没查到数据就直接返回空 JSON 或 200,掩盖了业务语义上的“不存在”。该 404 就得 404。