Golang HTTP服务中错误返回的标准方式

12次阅读

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

Golang HTTP服务中错误返回的标准方式

HTTP handler 中直接 return 错误不生效

gohttp.HandlerFunc 返回类型是 void,没有返回值接口,所以写 return err 是语法错误。常见误区是以为能像其他语言那样“抛出异常”或“中断流程”,实际必须手动调用 w.WriteHeader() + w.Write() 才算完成响应。

正确做法是:在出错时立即写入状态码和响应体,并避免后续逻辑继续执行(比如不要在 if err != nil 后面还调用 json.NewEncoder(w).Encode(...))。

  • 状态码优先用标准常量http.StatusbadRequesthttp.StatusNotFoundhttp.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.StatusBadRequesthttp.StatusUnauthorized)表示请求本身有问题,应由客户端修正;5xx 表示服务内部故障,需要后端排查。混用会导致监控失真、重试策略失效。

典型场景判断:

  • JSON 解析失败、字段缺失、ID 格式错误 → http.StatusBadRequest
  • 数据库连接失败、下游服务超时、空指针解引用 → http.StatusInternalServerError
  • JWT 过期或签名无效 → http.StatusUnauthorized(不是 403)
  • 资源不存在(如 GET /users/9999)→ http.StatusNotFound,而非 500

最常被忽略的是:没查到数据就直接返回空 JSON 或 200,掩盖了业务语义上的“不存在”。该 404 就得 404。

text=ZqhQzanResources