Go 一文帶你喫透 HTTP 客戶端!
1. HTTP 請求簡介
HTTP(Hypertext Transfer Protocol) 是構建 web 應用通信的基石。
HTTP 工作於客戶端 - 服務端架構上。
HTTP 客戶端發起請求, 服務器接收請求並返回響應。
HTTP 請求主要由請求行、請求頭、請求體組成
請求行 GET /search?name=Golang HTTP/1.1
請求頭部 Host: www.baidu.com
User-Agent: Mozilla/5.0
請求體 username=golang&password=123456
常用的 HTTP 方法
GET - 從服務器獲取資源
POST - 向服務器發送數據
PUT - 更新服務器上的資源
DELETE - 刪除服務器上的資源
HTTP 狀態碼
服務器返回的響應中都包含一個 HTTP 狀態碼, 常見的有:
200 OK - 請求成功
301 Moved Permanently - 重定向
404 Not Found - 資源未找到 500 Internal Server Error - 服務器內部錯誤
示例 GET 請求
發送 HTTP GET 請求獲取頁面, golang 標準庫 net/http 可以輕鬆實現
import "net/http"
func main() {
resp, err := http.Get("http://www.example.com")
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println(resp.Status) // HTTP狀態碼
}
2. 發送 HTTP 請求
Go 語言 net/http 包提供 HTTP 客戶端功能。主要使用兩個方法: Get 和 Post。
2.1 http.Get 發送 GET 請求
用 http.Get 發送 GET 請求到指定頁面, 然後打印響應狀態和頁面內容
func httpGetDemo() {
resp, err := http.Get("https://www.baidu.com")
if err != nil {
panic(err)
}
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(resp.Status)
fmt.Println(string(bytes))
}
注意程序在讀取完響應 Body 後調用 Close 方法關閉網絡連接。defer 用來保證連接被關閉。
2.2 http.Post 發送 POST 請求
POST 請求通常用於向服務器提交數據, 例如提交表單。下面代碼通過 POST 方式傳遞一個 form 字符串
func httpPostDemo() {
data := strings.NewReader(")
resp, err := http.Post("http://www.example.com/apply",
"application/x-www-form-urlencoded", data)
}
除了 form 表單, 還可以將 json、xml、protobuf 等格式的數據通過 body POST 到服務器。
2.3 自定義請求頭
可通過 Header 字段設置自定義頭信息
req, _ := http.NewRequest("GET", "http://example.com", nil)
// 設置Header
req.Header.Add("If-None-Match", `W/"wyzzy"`)
resp, _ := http.DefaultClient.Do(req)
添加多個 header 也很簡單
req.Header.Add("Accept", "text/html")
req.Header.Add("Connection", "close")
2.4 設置請求超時
Go 語言客戶端默認沒有超時時間, 可能導致長時間等待響應。利用 Timeout 字段可以設置超時
client := http.Client {
Timeout: 5 * time.Second, // 設置超時時間
}
resp, err := client.Get("https://www.slowwly.rocks")
以上 client 在發出請求後最多等待 5 秒, 如果服務器沒回應會返回錯誤。
3. 處理 HTTP 響應
發送請求後 equally 要正確處理服務器返回的響應數據。
解析響應
解析 HTTP 響應主要包括以下步驟
檢查狀態碼 StatusCode
讀取響應頭 Headers
讀取響應體 Body
代碼示例:
resp, _ := http.Get("https://www.google.com")
fmt.Println(resp.StatusCode)
for k, v := range resp.Header {
fmt.Println(k, v)
}
body, _ := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
響應體解析
很多 Web API 返回 JSON 格式的數據
{"error": 0, "msg": "success"}
Go 語言 解析 JSON 響應體很簡單
import "encoding/json"
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
可方便訪問結果數據, 類似的, 也可以將響應體解析爲 XML。
fmt.Println(result["error"])
fmt.Println(result["msg"])
4. 客戶端配置
4.1 Cookie
用 Cookie 字段可以管理和發送 HTTP cookie
client := &http.Client{}
var cookies []*http.Cookie
cookies = append(cookies, &http.Cookie{Name:"cookie1", Value:"val1"})
cookies = append(cookies, &http.Cookie{Name:"cookie2", Value:"val2"})
client.Jar = &cookiejar.Jar{}
client.Jar.SetCookies(&url.URL{}, cookies)
以後 client 發出的所有請求都會帶上這些 cookie。
4.2 代理
可以爲 client 配置 HTTP 代理
proxyURL, _ := url.Parse("http://proxy_host:port")
client := &http.Client{Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
}}
4.3 Redirect 策略
通過 CheckRedirect 函數可以自定義處理重定向的邏輯,允許重定向指定次數。
client := &http.Client{
CheckRedirect: func(
req *http.Request, via []*http.Request) error {
// 自定義Redirect策略
return http.ErrUseLastResponse
},
}
4.4 TLS 配置
自定義 Transport 可以控制 TLS 版本、密鑰、證書等設置
tr := &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: pool},
DisableCompression: true,
}
client := &http.Client{Transport: tr}
5. 高級技巧
5.1 連接複用
每次請求都建立新的 TCP 連接效率較低。爲此 HTTP 提供了連接複用機制。用 Client.Do 方法可以手動實現請求複用。
client := &http.Client{}
req1, _ := http.NewRequest("GET", "http://example.com", nil)
resp1, _ := client.Do(req1)
// 處理響應1
req2, _ := http.NewRequest("GET", "http://example.com/about", nil)
resp2, _ := client.Do(req2)
// 處理響應2
5.2 請求重試
要實現請求重試, 可在發送請求前檢查狀態, 錯誤超過一定次數則停止重試。這就可以包裝系統 HTTP client 實現請求重試邏輯。
// 自定義Client
type RetryClient struct {
HttpClient *http.Client
Retries int
}
func (c *RetryClient) Get(url string) (*http.Response, error) {
for i := 0; i <= c.Retries; i++ {
resp, err := c.HttpClient.Get(url)
// 如果沒有錯誤就返回響應
if err == nil {
return resp, nil
}
}
return nil, fmt.Errorf("request failed after %d retries", retries)
}
5.3 異步處理
對於耗時較長的請求, 可以啓動 Goroutine 異步發送, 這樣不會阻塞進程
func makeAsyncRequest() {
go func() {
resp, err := http.Get("http://example.com/data")
// 處理響應
}()
}
5.4 文件上傳
上傳文件需要設置請求爲 POST, 並將文件數據放到請求 body 發送
file, _ := os.Open("test.png")
fileContents, _ := ioutil.ReadAll(file)
resp, _ := http.Post("http://example.com/upload", "image/png", bytes.NewReader(fileContents))
5.5 分塊上傳大文件
上傳超大文件時, 可以分塊逐步上傳
file, _ := os.Open("bigfile.iso")
defer file.Close()
url := "http://example.com/upload"
buf := make([]byte, 1024)
for {
n, _ := file.Read(buf)
if n == 0 {
break
}
}
6. 請求示例
6.1 POST JSON 數據
程序構造一個 JSON 對象, 以 POST 方式提交到服務器
func postJSON() {
type Item struct {
Name string `json:"name"`
Price int `json:"price"`
}
itm := Item{"Apple", 5}
jsonData, _ := json.Marshal(itm)
resp, err := http.Post("https://api.example.com/create",
"application/json",
bytes.NewBuffer(jsonData))
}
6.2 文件下載
下載文件實際上就是發送 GET 請求, 然後保存響應 Body 到文件
func downloadFile() error {
resp, err := http.Get("http://example.com/file.zip")
if err != nil {
return err
}
defer resp.Body.Close()
// 創建文件
outFile, err := os.Create("./file.zip")
if err != nil {
return err
}
defer outFile.Close()
// 寫入響應流到文件
_, err = io.Copy(outFile, resp.Body)
return err
}
7. HTTP 客戶端庫比較
除了官方的 net/http 包, Go 語言還有很多優秀的 HTTP 客戶端庫
resty: 簡單好用的 HTTP 客戶端, 使用方法類似於 jQuery。
heimdallr: 提供重試機制和超時控制的強大 HTTP 客戶端。
sling: 靈活構造 HTTP 請求的客戶端工具。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/XcbOJnhkTAKJ8RCl2fbXzw