正确发送get请求须用http.client配context.withtimeout设超时,并读完resp.body后调close();url含中文等特殊字符需用url.values编码,不可直接拼接。

http.Get 发送 GET 请求的正确姿势
直接用 http.Get 是最简方式,但它默认不设超时、不处理重定向循环、也不自动关闭响应体——这些都会导致连接泄漏或程序卡死。
- 必须手动调用
resp.Body.Close(),否则底层 TCP 连接不会被复用,高并发下很快耗尽文件描述符 - 不设超时等同于“可能永远卡住”,尤其在 DNS 解析失败或服务无响应时;应改用
http.Client配合context.WithTimeout -
http.Get内部用的是默认http.DefaultClient,它的CheckRedirect默认允许最多 10 次重定向,但若后端返回恶意跳转链(比如 302 → 302 → …),仍会触发net/http: stopped after 10 redirects错误
带超时和错误处理的 GET 实现
别依赖 http.Get,自己构造 http.Client 才可控。超时不是可选项,是必选项。
- 用
context.WithTimeout包裹请求上下文,总超时建议设为 5–10 秒,避免阻塞 goroutine - 检查
err是否为context.DeadlineExceeded或net.ErrTimeout,区分是超时还是网络不通 - 即使
resp.StatusCode >= 400,也要读完resp.Body并关闭,否则连接无法释放;可用io.Copy(io.Discard, resp.Body)快速丢弃
ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.example.com/data", nil)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
// 处理 net.OpError、context.DeadlineExceeded 等
return
}
defer resp.Body.Close() // 必须放这里,哪怕后面 panic 也能执行
中文参数、特殊字符 URL 编码问题
URL 中含中文、空格、&、= 等字符时,直接拼接会 400 Bad Request;http.Get 不做任何编码,得自己来。
- 不要手写
"?q=" + keyword,用url.Values{}.Add()+url.ParseQuery()构建查询参数 - 完整 URL 应先用
url.Parse解析,再修改RawQuery字段,避免重复编码或漏编码 - 路径部分(path)和查询参数(query)编码规则不同:path 用
url.PathEscape,query 用url.QueryEscape
v := url.Values{}
v.Set("q", "Go语言入门")
v.Set("page", "1")
u, _ := url.Parse("https://api.example.com/search")
u.RawQuery = v.Encode()
resp, _ := http.Get(u.String()) // 此时 q=%E7%94%A8%E8%AF%AD%E5%85%A5%E9%97%A8 已正确编码
为什么响应体要读完才能关?
不读完 resp.Body 就调 Close(),HTTP 连接不会归还给连接池,下次同域名请求会新建连接,最终触发 dial tcp: too many open files。
-
http.Transport默认启用 Keep-Alive,但前提是它能判断响应已结束——这依赖你读完全部 body 或显式调用resp.Body.Close()后由底层完成清理 - 如果只关心状态码,也至少要
io.Copy(io.Discard, resp.Body)或io.ReadAll(resp.Body),不能跳过 - 注意:
resp.Body是io.ReadCloser,Close()是必须调的,哪怕前面已经读完了
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











