在Golang开发中,使用net/http标准库编写HTTP客户端时,请求过程涉及多个环节,每个环节都可能产生错误,只有全面捕获这些错误才能保证程序的稳定运行。

常见的HTTP客户端请求错误类型
HTTP客户端请求的错误可以分为几个阶段,不同阶段产生的错误需要不同的处理方式:
- 请求建立阶段错误:比如DNS解析失败、网络连接超时、无法连接到目标服务器等,这类错误通常在调用
http.Client的Do方法时直接返回。 - 响应读取阶段错误:比如服务端返回了错误状态码、读取响应体时发生IO错误、响应体过大导致读取异常等,这类错误需要在获取到响应后进一步判断。
- 上下文相关错误:如果使用带上下文的客户端请求,还可能因为上下文超时、被取消产生对应的错误。
基础错误捕获方法
最基础的捕获方式是先判断Do方法返回的错误是否为空,不为空则说明请求建立阶段出现了问题:
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
client := &http.Client{}
// 发起GET请求
resp, err := client.Get("http://ipipp.com/api/test")
// 优先捕获请求阶段的错误
if err != nil {
fmt.Printf("请求发生错误: %v\n", err)
return
}
// 确保响应体被关闭,避免资源泄漏
defer resp.Body.Close()
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("读取响应体发生错误: %v\n", err)
return
}
fmt.Printf("响应内容: %s\n", body)
}响应状态码的错误判断
即使Do方法没有返回错误,也不代表请求完全成功,还需要判断HTTP响应状态码,通常2xx状态码才表示请求成功:
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
client := &http.Client{}
resp, err := client.Get("http://ipipp.com/api/test")
if err != nil {
fmt.Printf("请求阶段错误: %v\n", err)
return
}
defer resp.Body.Close()
// 判断状态码是否为2xx
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Printf("服务端返回错误状态码: %d, 状态信息: %s\n", resp.StatusCode, resp.Status)
// 可以读取错误响应体进一步排查问题
errBody, _ := ioutil.ReadAll(resp.Body)
fmt.Printf("错误响应内容: %s\n", errBody)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("读取响应体错误: %v\n", err)
return
}
fmt.Printf("请求成功,响应内容: %s\n", body)
}带上下文的请求错误捕获
如果需要设置请求超时或者支持取消请求,可以使用context包,对应的错误可以通过判断错误类型来区分:
package main
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"time"
)
func main() {
client := &http.Client{}
// 创建超时上下文,设置3秒超时
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
// 创建带上下文的请求
req, err := http.NewRequestWithContext(ctx, "GET", "http://ipipp.com/api/test", nil)
if err != nil {
fmt.Printf("创建请求错误: %v\n", err)
return
}
resp, err := client.Do(req)
if err != nil {
// 判断是否为上下文超时错误
if ctx.Err() == context.DeadlineExceeded {
fmt.Println("请求超时")
return
}
// 判断是否为上下文取消错误
if ctx.Err() == context.Canceled {
fmt.Println("请求被取消")
return
}
fmt.Printf("其他请求错误: %v\n", err)
return
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Printf("服务端错误状态码: %d\n", resp.StatusCode)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("读取响应体错误: %v\n", err)
return
}
fmt.Printf("请求成功: %s\n", body)
}错误捕获的注意事项
- 每次获取到
resp后,不管是否出现错误,都要记得调用resp.Body.Close(),最好使用defer语句保证执行,避免连接资源泄漏。 - 读取响应体之前如果已经判断了状态码错误,读取完错误响应体后不需要再重复读取,否则会出现IO错误。
- 对于网络相关的错误,可以通过类型断言判断是否为
net.Error类型,进一步区分是超时错误还是其他网络错误。
错误捕获的核心逻辑是先处理请求阶段的错误,再判断响应状态码,最后处理响应体读取阶段的错误,根据业务需求还可以补充更细化的错误类型判断。
GolangHTTP_clienterror_capturenet_http修改时间:2026-06-06 16:58:22