http.Get仅适用于无超时、无头、无错误区分的玩具场景;生产环境必须用http.NewRequest+自定义http.Client,因其可设超时、Header、细粒度错误处理并避免连接泄漏。

Go 发 GET 请求最简单的方式是 http.Get,但它只适合“不设头、不超时、不重试、不关心错误细节”的玩具场景;真实项目里必须用 http.NewRequest + http.Client.Do。
为什么 http.Get 容易出问题
它看似一行搞定,但隐藏三个硬伤:
-
http.Get内部用的是默认http.DefaultClient,没有超时控制——请求卡住会永久阻塞 goroutine - 无法设置
User-Agent、Authorization等必要 header,很多 API 直接返回 403 - 错误处理模糊:网络失败、DNS 解析失败、TLS 握手失败全被包装成同一个
error,没法区分重试还是放弃
怎么写一个生产可用的 GET 请求
核心是自己构造 *http.Request 并用带配置的 *http.Client 发起:
client := &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
},
}
req, _ := http.NewRequest("GET", "https://api.example.com/data", nil)
req.Header.Set("User-Agent", "MyApp/1.0")
req.Header.Set("Accept", "application/json")
<p>resp, err := client.Do(req)
if err != nil {
// 区分是网络错误还是超时
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
log.Println("request timeout")
}
return
}
defer resp.Body.Close() // 必须加,否则连接泄漏</p><p>if resp.StatusCode != http.StatusOK {
log.Printf("unexpected status: %d", resp.StatusCode)
return
}</p><p>body, _ := io.ReadAll(resp.Body)
// 后续解析 JSON 或其他格式...</p>常见错误现象和修复点
这些不是“写错了”,而是 Go HTTP 客户端的惯性坑:
立即学习“go语言免费学习笔记(深入)”;
- 忘记
defer resp.Body.Close()→ 连接池耗尽,后续请求卡死或报too many open files - 用
ioutil.ReadAll(已弃用)→ 应改用io.ReadAll(Go 1.16+) - 直接打印
resp变量 → 输出类似&{200 OK ...},其实是结构体地址,真正数据在resp.Body流里 - 没检查
resp.StatusCode就直接解码 JSON → 服务端返回 500 错误页时,JSON 解码会 panic 或静默失败
什么时候真能用 http.Get
仅限以下情况:
- 本地开发调试,目标是
localhost的 mock 服务 - 脚本类一次性工具,且目标 URL 绝对稳定、无鉴权、无速率限制
- 教学示例中为降低认知负担(但要明确标注“仅演示,勿用于生产”)
真实接口调用几乎从不用它——多写三行代码换来健壮性,不亏。


















