HTTP handler 中不能 return 错误,须手动调用 w.WriteHeader() 和 w.Write() 发送响应;应封装 writeError 工具函数统一处理 JSON 错误格式,并用 recover 中间件兜底 panic,严格区分 4xx 与 5xx 状态码。

HTTP handler 中直接 return 错误不生效
Go 的 http.HandlerFunc 返回类型是 void,没有返回值接口,所以写 return err 是语法错误。常见误区是以为能像其他语言那样“抛出异常”或“中断流程”,实际必须手动调用 w.WriteHeader() + w.Write() 才算完成响应。
正确做法是:在出错时立即写入状态码和响应体,并避免后续逻辑继续执行(比如不要在 if err != nil 后面还调用 json.NewEncoder(w).Encode(...))。
- 状态码优先用标准常量:
http.StatusBadRequest、http.StatusNotFound、http.StatusInternalServerError - 响应体建议统一 JSON 格式,包含
error字段,例如:{"error": "invalid ID format"} - 不要忘记在写完响应后
return,防止后续代码意外写入已关闭的 ResponseWriter
封装统一的 error response 工具函数
重复写 w.WriteHeader() + json.Marshal() + w.Write() 容易遗漏或不一致。推荐封装一个 writeError(w http.ResponseWriter, status int, msg string) 函数,集中控制格式和 Content-Type。
注意点:
立即学习“go语言免费学习笔记(深入)”;
- 务必先设置
w.Header().Set("Content-Type", "application/json; charset=utf-8"),否则前端可能解析失败 - 状态码要在
w.Write()前调用w.WriteHeader(status);如果已经写过响应体,再调用会静默忽略 - 避免在工具函数里 panic 或 log.Fatal —— 这会让整个服务崩溃
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 并转为 500 错误
未捕获的 panic 会导致连接被关闭,客户端收不到任何响应。生产环境必须用中间件兜底:
- 用
defer/recover捕获 handler 内 panic - 恢复后仍要调用
w.WriteHeader(http.StatusInternalServerError),否则默认是 200 - 日志中记录 panic stack,但响应体不要暴露敏感信息(如文件路径、变量值)
func 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)
})
}
区分客户端错误与服务端错误,别滥用 500
4xx 错误(如 http.StatusBadRequest、http.StatusUnauthorized)表示请求本身有问题,应由客户端修正;5xx 表示服务内部故障,需要后端排查。混用会导致监控失真、重试策略失效。
典型场景判断:
- JSON 解析失败、字段缺失、ID 格式错误 →
http.StatusBadRequest - 数据库连接失败、下游服务超时、空指针解引用 →
http.StatusInternalServerError - JWT 过期或签名无效 →
http.StatusUnauthorized(不是 403) - 资源不存在(如 GET /users/9999)→
http.StatusNotFound,而非 500
最常被忽略的是:没查到数据就直接返回空 JSON 或 200,掩盖了业务语义上的“不存在”。该 404 就得 404。










