前言
標準庫 net/http
發送 HTTP 請求時,會執行以下 3 個步驟:
- 建構請求:調用
http.NewRequest
或 http.NewRequestWithContext
函式,根據傳入的 Context、Method、URL 和 Request Body 等參數創建一個請求。
- 開始事務:調用
http.Transport.RoundTrip
開啟 HTTP 事務、獲取連接並發送請求。
- 等待響應:在 HTTP 持久連接的
http.persistConn.writeLoop
中等待響應。
介面
標準庫 net/http
實現了以下幾個重要的介面:
http.RoundTripper
:用於發出一個 HTTP 請求,客戶端將 Request
物件做為參數傳入,就會發出該請求,並從服務端獲取相對應的響應或錯誤。稱之為「往返者」。
1 2 3
| type RoundTripper interface { RoundTrip(*Request) (*Response, error) }
|
http.Handler
:用於響應客戶端發出的請求,實現了處理請求的實際商業邏輯,最後還會調用 http.ResponseWriter
介面的方法,來創造一個相應的響應或錯誤。稱之為:「處理器」。
1 2 3
| type Handler interface { ServeHTTP(ResponseWriter, *Request) }
|
http.ResponseWriter
:提供了三個方法 Header
、Write
和 WriteHeader
分別用於獲取 HTTP 響應頭、響應主體和設置 Status Code。
1 2 3 4 5
| type ResponseWriter interface { Header() Header Write([]byte) (int, error) WriteHeader(statusCode int) }
|
結構體
標準庫 net/http
實現了以下幾個重要的結構體:
http.Client
:表示 HTTP 客戶端,實現了包括 Cookies 和重定向等協議內容。默認使用 http.DefaultTransport
,也可以自訂一個 Client
。
http.Transport
:實現了 http.RoundTripper
介面,包括:連接重用、建構請求、發送請求和 HTTP Proxy 等功能。
http.persistConn
:是 TCP 協議長連接功能的封裝,做為客戶端與服務端交換 HTTP Message(消息)的控制(handle)。
做法
發送一個簡單的 HTTP 請求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package main
import ( "bytes" "fmt" "log" "net/http" "reflect" )
func main() { resp, err := http.Get("http://google.com/")
if err != nil { log.Println(err) return }
defer resp.Body.Close()
headers := resp.Header
for k, v := range headers { fmt.Printf("k=%v, v=%v\n", k, v) }
fmt.Printf("resp Status %s\n", resp.Status) fmt.Printf("resp StatusCode %d\n", resp.StatusCode) fmt.Printf("resp Proto %s\n", resp.Proto) fmt.Printf("resp ContentLength %d\n", resp.ContentLength) fmt.Printf("resp TransferEncoding %v\n", resp.TransferEncoding) fmt.Printf("resp Uncompressed %t\n", resp.Uncompressed)
fmt.Println(reflect.TypeOf(resp.Body))
buf := bytes.NewBuffer(make([]byte, 0, 512)) length, _ := buf.ReadFrom(resp.Body)
fmt.Println(len(buf.Bytes())) fmt.Println(length) fmt.Println(string(buf.Bytes())) }
|
使用 http.NewRequest
創建一個 Request
物件,再透過 http.Client
執行這個 Request
物件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| package main
import ( "bytes" "fmt" "log" "net/http" "reflect" )
func main() { client := &http.Client{} req, err := http.NewRequest(http.MethodGet, "http://google.com/", nil)
if err != nil { fmt.Println("Fatal error ", err.Error()) }
resp, err := client.Do(req)
if err != nil { log.Println(err) return }
defer resp.Body.Close()
headers := resp.Header
for k, v := range headers { fmt.Printf("k=%v, v=%v\n", k, v) }
fmt.Printf("resp Status %s\n", resp.Status) fmt.Printf("resp StatusCode %d\n", resp.StatusCode) fmt.Printf("resp Proto %s\n", resp.Proto) fmt.Printf("resp ContentLength %d\n", resp.ContentLength) fmt.Printf("resp TransferEncoding %v\n", resp.TransferEncoding) fmt.Printf("resp Uncompressed %t\n", resp.Uncompressed)
fmt.Println(reflect.TypeOf(resp.Body))
buf := bytes.NewBuffer(make([]byte, 0, 512)) length, _ := buf.ReadFrom(resp.Body)
fmt.Println(len(buf.Bytes())) fmt.Println(length) fmt.Println(string(buf.Bytes())) }
|
參考資料