Go中用http.Client发起JSON请求需手动序列化、设Content-Type头、检查状态码;解析响应时应先读取完整body,用指针或json.RawMessage处理null和嵌套字段;须复用带超时和连接池配置的全局client。

Go中用http.Client发起JSON请求的典型写法
Go标准库不自动处理JSON序列化/反序列化,必须手动编码请求体、设置Content-Type头,并检查响应状态码。直接用http.Post容易忽略错误分支和超时控制。
- 始终用
&http.Client{Timeout: 10 * time.Second}显式设超时,避免协程永久阻塞 - 请求体必须是
[]byte,用json.Marshal生成,不能传结构体指针直接给bytes.NewReader - 务必检查
resp.StatusCode是否为2xx,http.Post只判断网络层错误,不校验HTTP状态码 -
Content-Type头必须设为"application/json; charset=utf-8",部分API(如GitHub)严格校验该字段
client := &http.Client{Timeout: 10 * time.Second}
data := map[string]string{"name": "alice", "age": "30"}
body, _ := json.Marshal(data)
req, _ := http.NewRequest("POST", "https://api.example.com/users", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json; charset=utf-8")
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Fatalf("HTTP %d", resp.StatusCode)
}用json.Unmarshal解析响应时的常见panic
直接对resp.Body调用json.Unmarshal会panic:如果响应不是合法JSON,或结构体字段标签与JSON key不匹配,或遇到null值但目标字段是非nil类型(如string而非*string)。
- 先用
ioutil.ReadAll(Go 1.16+ 改用io.ReadAll)读取完整body,避免后续多次读取空内容 - 检查
err是否为json.SyntaxError,可定位到具体出错位置 - 对可能为
null的字段,用指针类型(*string)或sql.NullString类包装,避免解码失败 - 用
json.RawMessage延迟解析嵌套JSON字段,防止结构体定义不全导致崩溃
body, _ := io.ReadAll(resp.Body)
var result struct {
ID int `json:"id"`
Name *string `json:"name"` // 允许null
Extra json.RawMessage `json:"extra,omitempty"`
}
if err := json.Unmarshal(body, &result); err != nil {
if syntaxErr, ok := err.(*json.SyntaxError); ok {
log.Printf("JSON syntax error at offset %d", syntaxErr.Offset)
}
log.Fatal(err)
}如何安全复用http.Client并管理连接池
每次新建http.Client会导致TCP连接无法复用,短连接频繁创建销毁,触发too many open files错误。默认http.DefaultClient虽可复用,但无超时控制,生产环境禁用。
- 全局复用一个
*http.Client实例,不要按请求新建 - 通过
Transport配置连接池:MaxIdleConns和MaxIdleConnsPerHost设为100起,避免连接耗尽 - 设
IdleConnTimeout为30 * time.Second,防止TIME_WAIT堆积 - 若需带认证(如Bearer Token),用
context.WithValue传token,而非在Client上硬编码Header
var httpClient = &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 30 * time.Second,
},
}接口返回非标准JSON(如带BOM、HTML错误页)怎么办
很多HTTP API在出错时不返回JSON,而是返回HTML错误页或纯文本,直接json.Unmarshal会报invalid character '。不能依赖Content-Type头,必须先检查响应内容。
- 读取body后,用
bytes.HasPrefix(body, []byte{'{','[','"'})粗略判断是否为JSON - 若不是JSON且
resp.StatusCode >= 400,应把body当错误信息处理,而不是强转结构体 - 对含UTF-8 BOM的响应(
\xEF\xBB\xBF),用bytes.TrimPrefix(body, []byte{0xEF, 0xBB, 0xBF})清理 - 避免用
strings.Contains(string(body), "error")判断错误,易误判正常JSON中的字段名
真正麻烦的是那些文档写JSON、实际返回混合格式的第三方API——得靠日志记录原始body才能定位问题。










