引言:为什么在Go中掌握HTTP工作很重要
\nGo(Golang)是一种为网络工作而设计的语言。其标准库net/http提供了强大且灵活的工具,用于创建服务器和客户端。然而,许多开发者,尤其是从脚本语言转来的,常犯典型错误:忘记关闭响应体、未设置超时或错误处理不当。
在本文中,我们将从A到Z讲解如何在Go中创建HTTP客户端。您将学习如何正确发送GET和POST请求、管理标头、配置超时以及处理错误。所有代码示例均可直接使用。
\n\n1. 基础:创建客户端和第一个GET请求
\n在Go中,HTTP客户端是一个http.Client结构体。您可以使用全局客户端http.DefaultClient,或创建具有自定义设置的自有客户端。我们从简单的GET请求开始。
package main\n\nimport (\n "fmt"\n "io"\n "net/http"\n "os"\n)\n\nfunc main() {\n // 使用内置客户端\n resp, err := http.Get("https://api.example.com/data")\n if err != nil {\n fmt.Printf("请求错误:%v\\n", err)\n os.Exit(1)\n }\n defer resp.Body.Close() // 重要:始终关闭响应体!\n\n // 读取响应体\n body, err := io.ReadAll(resp.Body)\n if err != nil {\n fmt.Printf("读取响应错误:%v\\n", err)\n os.Exit(1)\n }\n\n fmt.Printf("状态:%s\\n", resp.Status)\n fmt.Printf("响应体:%s\\n", string(body))\n}\n关键点:
\n- \n
- 始终通过
defer关闭resp.Body。否则会导致资源泄漏(连接无法返回连接池)。 \n - 检查响应状态(
resp.StatusCode)。200不是唯一成功的状态码(还有201、204等)。 \n io.ReadAll将整个响应体读入内存。对于大型响应,请使用io.Copy或流式处理。 \n
2. 高级客户端:超时、标头和自定义设置
\n全局客户端默认没有超时——这可能导致程序挂起。始终创建具有设置的自有客户端。
\n2.1. 创建带超时的客户端
\nclient := &http.Client{\n Timeout: 10 * time.Second, // 整个请求的总超时\n}\n\nresp, err := client.Get("https://api.example.com/data")\n还可以通过Transport在不同级别配置超时:
transport := &http.Transport{\n DialContext: (&net.Dialer{\n Timeout: 5 * time.Second, // 连接超时\n KeepAlive: 30 * time.Second,\n }).DialContext,\n TLSHandshakeTimeout: 5 * time.Second,\n ResponseHeaderTimeout: 5 * time.Second,\n}\n\nclient := &http.Client{\n Transport: transport,\n Timeout: 15 * time.Second,\n}\n\n2.2. 添加标头和参数
\n对于复杂请求,使用http.NewRequest:
req, err := http.NewRequest("GET", "https://api.example.com/search", nil)\nif err != nil {\n log.Fatal(err)\n}\n\n// 添加标头\nreq.Header.Set("Authorization", "Bearer your-token")\nreq.Header.Set("Accept", "application/json")\n\n// 添加查询参数\nq := req.URL.Query()\nq.Add("query", "golang")\nq.Add("page", "1")\nreq.URL.RawQuery = q.Encode()\n\nresp, err := client.Do(req)\n\n3. 使用POST和其他方法(JSON、表单)
\nPOST请求需要传递请求体。我们来看两种流行格式:JSON和URL编码表单。
\n3.1. 带JSON的POST
\ntype RequestData struct {\n Name string `json:"name"`\n Email string `json:"email"`\n}\n\ndata := RequestData{\n Name: "伊万",\n Email: "ivan@example.com",\n}\n\njsonData, err := json.Marshal(da