
本文详解如何在 go 中安全、完整地读取 post 请求的 http 响应体(包括 4xx/5xx 错误状态下的 json 错误信息),避免因忽略响应体导致关键错误详情丢失。
本文详解如何在 go 中安全、完整地读取 post 请求的 http 响应体(包括 4xx/5xx 错误状态下的 json 错误信息),避免因忽略响应体导致关键错误详情丢失。
在调用 RESTful API 的 POST 方法时,开发者常误以为只有 resp.StatusCode == 200 时才需读取响应体,而对 401 Unauthorized 等错误状态直接跳过解析。但正如示例所示,即使认证失败,服务端通常仍会返回结构化 JSON 错误响应(如含 "errorMessages" 字段),这对调试和用户提示至关重要。
正确做法是:无论状态码如何,只要 resp.Body 非 nil,都应主动读取并关闭。以下是修正后的完整实现:
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
func postUrl(url string, byt []byte) (map[string]interface{}, error) {
tr := &http.Transport{
DisableCompression: true,
}
client := &http.Client{
Transport: tr,
Timeout: 10 * time.Second,
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(byt))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("X-Custom-Header", "myvalue")
req.Header.Set("Content-Type", "application/json")
req.Header.Add("Authorization", "Basic "+basicAuth("username", "password"))
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close() // ✅ 关键:始终关闭 Body
// ✅ 关键:始终读取响应体(即使 status != 200)
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
// 可选:根据状态码分类处理
if resp.StatusCode >= 400 {
var errResp struct {
ErrorMessages []string `json:"errorMessages"`
Errors map[string]string `json:"errors"`
}
if json.Unmarshal(body, &errResp) == nil {
return nil, fmt.Errorf("API error %d: %v", resp.StatusCode, errResp.ErrorMessages)
}
return nil, fmt.Errorf("API error %d (non-JSON body)", resp.StatusCode)
}
// 成功响应:解析为通用 map 或自定义结构体
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("failed to parse JSON response: %w", err)
}
return result, nil
}
func basicAuth(username, password string) string {
auth := username + ":" + password
return base64.StdEncoding.EncodeToString([]byte(auth))
}⚠️ 重要注意事项:
-
必须调用
defer resp.Body.Close():否则会导致连接泄漏,尤其在高并发场景下迅速耗尽资源; -
不要依赖
resp.StatusCode判断是否读取 Body:HTTP 协议允许服务端在任意状态码下返回有效载荷; -
使用
io.ReadAll(Go 1.16+)替代已弃用的ioutil.ReadAll; - 若需结构化错误处理,建议定义明确的错误响应结构体,而非仅用
map[string]interface{}; - 生产环境应添加重试、日志记录及更细粒度的错误分类(如网络错误 vs 认证失败 vs 业务校验失败)。
通过以上实践,你不仅能获取 200 OK 下的成功数据,更能精准捕获并解析 401、403、404 等常见错误中的 JSON 提示,显著提升 API 集成的健壮性与可维护性。


















