Go http 包詳解
Go 語言中的 http 包提供了創建 http 服務或者訪問 http 服務所需要的能力,不需要額外的依賴。在這篇文章中,我們會介紹這些功能的使用,以及看一下 http 包的設計思路。
- http 的客戶端
1.1 發送普通請求
在 Go 語言中發送請求很簡單,如果不需要額外的配置,可以直接使用 http 包封裝的 http Client 發送請求,比如發送 GET 請求:
resp, _ := http.Get("https://golang.org")
defer resp.Body.Close()
發送 POST ,並攜帶 JSON 數據的請求:
data := make(map[string]string)
dataJson, _ := json.Marshal(data)
reader := bytes.NewBuffer(dataJson)
resp, _ := http.Post("https://golang.org", "application/json;charset=utf-8", reader)
defer resp.Body.Close()
發送 POST 表單請求:
resp, _ := http.PostForm("https://golang.org", url.Values{"username":{"rayjun"}, "password":{"password"}})
defer resp.Body.Close()
在每個請求發完之後,需要手動關閉響應。
1.2 客戶端配置
在實際使用的過程中,我們通常不會直接上面的方法,而是會自己做一些 Client 的配置,比如調整超時時間:
client := &http.Client{
Timeout: 5 * time.Second,
}
resp, _ := client.Get("https://golang.org")
defer resp.Body.Close()
另外在很多時候,我們需要使用 GET 和 POST 之外的 http 方法,那就需要下面這樣的配置:
client := &http.Client{
Timeout: 5 * time.Second,
}
req, _ := http.NewRequest("PUT", "https://golang.org", nil)
resp, _ := client.Do(req)
defer resp.Body.Close()
比如還需要在請求的 Header 中增加一些字段:
client := &http.Client{
Timeout: 5 * time.Second,
}
req, _ := http.NewRequest("GET", "https://golang.org", nil)
req.Header.Add("User-Id", "userid123456")
resp, _ := client.Do(req)
defer resp.Body.Close()
或者更進一步,我們需要自定義傳輸層的一些配置:
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}
client := &http.Client{Transport: tr}
resp, _ := client.Get("https://golang.org")
defer resp.Body.Close()
http 包中發送請求,提供了不同層次的配置,滿足不同場景的使用。
- http 的服務端
除了客戶端,使用 http 包來創建 http 服務也很方便。
2.1 一行代碼創建 http 服務
創建一個 http 服務,在 Go 代碼中,只需要一行代碼:
func main() {
http.ListenAndServe(":8080", nil)
}
在 main 方法中,寫下上面那行代碼,然後運行 main 方法,端口號爲 8080 的 http 服務就運行起來了, 但目前還處理不了任何請求。
2.2 添加請求路徑
在上面代碼的基礎上,需要添加一個路徑,這樣服務纔可以開始處理請求:
func main() {
http.Handle("/index", &CustomerHandler{})
http.ListenAndServe(":8080", nil)
}
type CustomerHandler struct {
}
func (c *CustomerHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
fmt.Println("implement http server by self")
writer.Write([]byte("server echo"))
}
添加了 /index
路徑,在這種方式下,需要爲每一個請求都定義一個 Handler,然後 Handler 需要實現 ServeHttp 方法。
Handler 是一個請求處理器,我們如果使用這種方式,就需要爲每一個請求的 url 實現一個 Handler,這樣實現很繁瑣。
但我們還有另一個選擇,就是使用 HandlerFunc,添加另外一個路徑:
func main() {
http.HandleFunc("/index", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("HandleFunc implement"))
})
http.ListenAndServe(":8080", nil)
}
使用這種方式很簡潔,值需要實現 HandlerFunc 類型的一個匿名方法就可以了,HandlerFunc 是一個適配器,可以讓我們把一個與 ServeHTTP 簽名相同的函數作爲一個處理器。
Handler 和 HandlerFunc 都是通過 DefaultServeMux 來實現的。DefaultServeMux 纔是上面服務的核心。
在上面的代碼,http.ListenAndServe 的第二個參數傳入的是 nil,通常情況下,這個參數都是 nil,跟進代碼,發現這個參數爲 nil 的時候,就是使用 DefaultServeMux 來作爲服務端的實現:
// server.go
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
DefaultServeMux 的類型是 ServeMux,這是 Go 語言原生包中 http 服務端的默認實現。ServeMux 同樣實現了 ServeHttp 這個方法。
ServeHttp 方法纔是整個 http 服務的核心,只要需要處理請求,就必須實現這個方法。Handler 和 HandlerFunc 只是 Go 語言提供的兩種實現。
- http 的反向代理
反向代理在開發 Web 應用,特別是開發網關類應用的時候會經常用到, Go 也提供了實現,基本上開箱即用。
func main() {
http.HandleFunc("/formawd", func(writer http.ResponseWriter, request *http.Request) {
director := func(req *http.Request) {
req.URL.Scheme = "https"
req.URL.Host = "golang.org"
req.URL.Path = "upload"
}
proxy := &httputil.ReverseProxy{Director: director}
proxy.ServeHTTP(writer, request)
})
http.ListenAndServe(":8080", nil)
}
上面的代碼會把所有的請求都轉發到一個地方,當然也可以通過配置,將請求轉發到不同的地方。
- 小結
Go 語言原生的包就自帶了 http 包,這個包提供 http 編程所需要的基礎能力,開箱即用,不需要額外的依賴。在實際項目中使用,做個簡單的封裝即可。而且還自帶反向代理的能力,可以很方便的寫出一個 API 網關。
文 / Rayjun
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/viO0tCl59C4yiRs738Flfw