Go net/http需显式处理资源生命周期:必须Close请求/响应Body、Header设于WriteHeader前、自定义Client设超时;路由用显式ServeMux、JSON用流式解码、响应用json.Encoder。

Go 的 net/http 包开箱即用,不需要任何第三方依赖就能跑起一个生产级 HTTP 服务或客户端——但“能跑”和“跑稳”之间,差的往往是几行错误处理、一次 Body.Close() 或一个被忽略的 Header() 调用顺序。
怎么注册路由:别只用 http.HandleFunc,优先显式用 http.ServeMux
默认的 http.DefaultServeMux 是全局单例,所有 http.HandleFunc 都往它上面注册。这在小脚本里没问题,但在中大型项目里容易污染、难测试、无法隔离中间件。
- 显式创建
http.ServeMux:更清晰、可复用、支持子路由前缀(比如http.StripPrefix("/api", apiHandler)) - 路径匹配是前缀匹配:
"/users"会匹配/users/123和/users?name=alice;而"/users/"(结尾带斜杠)才表示“子路径模式” - 不支持通配符(如
/users/{id}),需要自己解析r.URL.Path或引入路由库
怎么读请求体:别用 io.ReadAll(r.Body) 直接硬读
r.Body 是个 io.ReadCloser,只能读一次,且不关会泄漏连接。对 JSON 请求,json.NewDecoder(r.Body).Decode(&v) 是更省内存、更安全的选择——它流式解析,不把整个请求体加载进内存。
- 必须
defer r.Body.Close(),哪怕你用json.NewDecoder—— 它内部不会自动关闭 - 不要在 decode 前调用
r.ParseForm()或r.FormValue(),它们会提前消费Body,导致后续 decode 失败 - 若需同时支持表单和 JSON,先检查
r.Header.Get("Content-Type")再分支处理
怎么写响应体:状态码、头、正文三者有严格时序
w.Header().Set() 必须在 w.WriteHeader() 或第一次 w.Write() 之前调用,否则会被静默忽略。很多“Content-Type 不生效”的问题都源于此。
立即学习“go语言免费学习笔记(深入)”;
- 推荐统一用
json.NewEncoder(w).Encode(v)输出 JSON:自动处理编码、写入、错误返回,比json.Marshal+w.Write更健壮 - 手动设状态码:
w.WriteHeader(http.StatusCreated),别依赖默认 200;尤其 POST 成功后应返回 201 - 设置头信息时,
"Content-Type"推荐带charset=utf-8,比如"application/json; charset=utf-8"
怎么发 HTTP 客户端请求:永远别用 http.Get 上生产
http.Get 用的是 http.DefaultClient,没有超时控制。网络卡住时,goroutine 就永远挂起,最终耗尽资源。
- 必须自定义
*http.Client,至少设Timeout(例如10 * time.Second) - 务必
defer resp.Body.Close(),且要放在if err == nil分支内第一行,防止resp为nil时 panic - 大响应体建议用
io.LimitReader(resp.Body, maxBytes)防 OOM,别等io.ReadAll把几百 MB 全读进内存
最常被跳过的细节不是语法,而是资源生命周期:r.Body 要关、resp.Body 要关、Header() 要在写之前设、ListenAndServe 的错误必须 log.Fatal 或显式处理——这些地方漏一个,服务就可能在线上安静地掉链子。










