写点什么

Go http 包详解

用户头像
Rayjun
关注
发布于: 2021 年 06 月 11 日
Go http 包详解

Go 语言中的 http 包提供了创建 http 服务或者访问 http 服务所需要的能力,不需要额外的依赖。在这篇文章中,我们会介绍这些功能的使用,以及看一下 http 包的设计思路。

1. 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 包中发送请求,提供了不同层次的配置,满足不同场景的使用。

2. 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.gofunc (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 语言提供的两种实现。

3. 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)}
复制代码


上面的代码会把所有的请求都转发到一个地方,当然也可以通过配置,将请求转发到不同的地方。

4. 小结

Go 语言原生的包就自带了 http 包,这个包提供 http 编程所需要的基础能力,开箱即用,不需要额外的依赖。在实际项目中使用,做个简单的封装即可。而且还自带反向代理的能力,可以很方便的写出一个 API 网关。


文 / Rayjun

发布于: 2021 年 06 月 11 日阅读数: 11
用户头像

Rayjun

关注

程序员,王小波死忠粉 2017.10.17 加入

非著名程序员,还在学习如何写代码,公众号同名

评论

发布
暂无评论
Go http 包详解