http.ListenAndServe端口被占会返回错误而非静默失败,必须检查error;路径匹配为前缀式;r.Body是一次性流需缓存;HTTP/2仅限TLS环境启用。

用 http.ListenAndServe 启动最简服务,但端口被占会静默失败
Go 的 HTTP 服务器默认不报错退出,http.ListenAndServe 在端口已被占用时返回 http: Server closed 或更常见的 listen tcp :8080: bind: address already in use,但如果你没检查返回值,程序看似运行了,实则根本没监听。
- 必须检查
http.ListenAndServe的 error 返回:它不是“成功才返回 nil”,而是“失败才返回非 nil” - 开发时建议加日志:
log.Fatal(http.ListenAndServe(":8080", nil)),避免静默挂掉 - 端口为
":8080"是常见写法,但不能写成"8080"(缺冒号)或"localhost:8080"(部分系统下 IPv6 行为不一致)
路由用 http.HandleFunc 就够用,但路径匹配是前缀式而非全匹配
http.HandleFunc 的路径参数是前缀匹配,比如注册了 "/api",那 /api/users、/api/v1/xxx 都会命中——这不是 bug,是设计如此。真要精确匹配,得自己判断 r.URL.Path。
- 注册
"/"会捕获所有未匹配路径,适合做兜底;但别在它前面注册更宽泛的路径,否则后面注册的会被忽略 - 不要依赖
http.ServeMux的顺序:它按注册顺序遍历,但前缀匹配优先级高于顺序,容易误判 - 生产环境建议换
gorilla/mux或chi,它们支持{id}占位符和严格路径匹配
处理请求时别直接读 r.Body 多次,它是一次性流
r.Body 是 io.ReadCloser,底层通常是 socket 连接,读完就 EOF。第二次 io.ReadAll(r.Body) 会得到空字节切片,且不报错——这是最常踩的坑。
- 如果需要多次访问请求体(比如日志 + 解析),先用
io.ReadAll读一次,存成变量,再反复用 - 记得调用
defer r.Body.Close(),否则连接不释放,压测时很快耗尽文件描述符 - 对 JSON 请求,直接用
json.NewDecoder(r.Body).Decode(&v)更安全,它内部处理了流读取,但依然不能重复调用
HTTP/2 和 TLS 不是自动开启的,http.ListenAndServeTLS 才触发
Go 1.8+ 默认支持 HTTP/2,但**仅限 TLS 环境**。用 http.ListenAndServe 走明文 HTTP,哪怕客户端支持 HTTP/2,也只会走 HTTP/1.1。
立即学习“go语言免费学习笔记(深入)”;
- 启用 HTTPS 必须用
http.ListenAndServeTLS(":443", "cert.pem", "key.pem"),证书路径必须可读 - 自签名证书开发时可用,但浏览器会警告;正式环境务必用 Let's Encrypt 等可信证书
- HTTP/2 不支持 HTTP/1.1 的 Upgrade 机制,所以 WebSocket 在 HTTP/2 下需通过
h2c(明文 HTTP/2)或反向代理(如 Nginx)中转
真正麻烦的从来不是写几行 http.HandleFunc,而是默认行为和边界条件——比如 r.Body 的一次性、路径匹配逻辑、错误不抛出、HTTP/2 的 TLS 绑定。这些点不亲手撞过几次,很难记住。


















