Golang中如何处理HTTP响应最佳实践
Golang中如何处理HTTP响应最佳实践 一个标准的 HTTP 请求如下:
data, _ := json.Marshal(map[string]any{
"passwd": "123",
"name": "xx",
})
req, err := http.Post("http://localhost:8085/login", "application/json", bytes.NewReader(data))
if err != nil {
panic(err)
}
defer req.Body.Close()
io.Copy(io.Discard, req.Body) // 必须这样做吗?
我需要发起很多 HTTP 请求。如果我不关心响应,我应该使用 io.Copy(io.Discard, req.Body) 吗?
我看到一些文章说 resp.Body.Close() 会读取并丢弃剩余的响应体数据。
更多关于Golang中如何处理HTTP响应最佳实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
在Go中处理HTTP响应时,确保正确关闭响应体是关键。以下是处理HTTP响应的最佳实践:
1. 必须关闭响应体
即使不关心响应内容,也必须调用resp.Body.Close():
resp, err := http.Post("http://localhost:8085/login", "application/json", bytes.NewReader(data))
if err != nil {
panic(err)
}
defer resp.Body.Close() // 必须调用
2. 完全读取响应体
如果不读取响应体,连接可能无法被复用。以下是正确做法:
// 方法1:使用io.Copy完全读取
resp, err := http.Post(url, contentType, body)
if err != nil {
return err
}
defer resp.Body.Close()
_, err = io.Copy(io.Discard, resp.Body)
if err != nil {
return err
}
// 方法2:使用io.ReadAll(Go 1.16+)
resp, err := http.Post(url, contentType, body)
if err != nil {
return err
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
_ = bodyBytes // 如果不关心内容
3. 批量请求的优化方案
对于大量HTTP请求,建议:
func makeRequest(url string, data []byte) error {
resp, err := http.Post(url, "application/json", bytes.NewReader(data))
if err != nil {
return err
}
defer resp.Body.Close()
// 必须完全读取响应体
_, err = io.Copy(io.Discard, resp.Body)
return err
}
// 使用sync.Pool复用缓冲区
var bufferPool = sync.Pool{
New: func() interface{} {
return bytes.NewBuffer(make([]byte, 0, 4096))
},
}
func makeRequestWithPool(url string, data map[string]any) error {
buf := bufferPool.Get().(*bytes.Buffer)
defer bufferPool.Put(buf)
defer buf.Reset()
json.NewEncoder(buf).Encode(data)
resp, err := http.Post(url, "application/json", buf)
if err != nil {
return err
}
defer resp.Body.Close()
_, err = io.Copy(io.Discard, resp.Body)
return err
}
4. 使用http.Client控制连接
对于高频请求,配置自定义的http.Client:
client := &http.Client{
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
},
Timeout: 10 * time.Second,
}
func makeRequestWithClient(client *http.Client, url string, data []byte) error {
resp, err := client.Post(url, "application/json", bytes.NewReader(data))
if err != nil {
return err
}
defer resp.Body.Close()
_, err = io.Copy(io.Discard, resp.Body)
return err
}
5. 错误处理完整示例
func sendLoginRequest(username, password string) error {
data, err := json.Marshal(map[string]string{
"name": username,
"passwd": password,
})
if err != nil {
return fmt.Errorf("marshal failed: %w", err)
}
resp, err := http.Post("http://localhost:8085/login",
"application/json",
bytes.NewReader(data))
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
// 检查HTTP状态码
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("server returned %d: %s",
resp.StatusCode, string(body))
}
// 如果不关心响应体但需要保持连接可复用
_, err = io.Copy(io.Discard, resp.Body)
if err != nil {
return fmt.Errorf("reading response failed: %w", err)
}
return nil
}
关键点:
- 必须调用
defer resp.Body.Close() - 必须完全读取响应体(使用
io.Copy(io.Discard, resp.Body)或io.ReadAll) - 错误处理要完整
- 对于大量请求,使用连接池和缓冲区池优化性能


