语言内置的net/http包十分的优秀,提供了HTTP客户端和服务端的实现。

1. net/http介绍

Go语言内置的net/http包提供了HTTP客户端和服务端的实现。

HTTP协议

超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。

2. HTTP客户端

基本的HTTP/HTTPS请求

Get、Head、Post和PostForm函数发出HTTP/HTTPS请求。

  1. resp, err := http.Get("http://example.com/")
  2. ...
  3. resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
  4. ...
  5. resp, err := http.PostForm("http://example.com/form",
  6. url.Values{"key": {"Value"}, "id": {"123"}})

程序在使用完response后必须关闭回复的主体。

  1. resp, err := http.Get("http://example.com/")
  2. if err != nil {
  3. // handle error
  4. }
  5. defer resp.Body.Close()
  6. body, err := ioutil.ReadAll(resp.Body)
  7. // ...

GET请求示例

使用net/http包编写一个简单的发送HTTP请求的Client端,代码如下:

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. )
  7. func main() {
  8. resp, err := http.Get("https://www.liwenzhou.com/")
  9. if err != nil {
  10. fmt.Printf("get failed, err:%v\n", err)
  11. return
  12. }
  13. defer resp.Body.Close()
  14. body, err := ioutil.ReadAll(resp.Body)
  15. if err != nil {
  16. fmt.Printf("read from resp.Body failed, err:%v\n", err)
  17. return
  18. }
  19. fmt.Print(string(body))
  20. }

将上面的代码保存之后编译成可执行文件,执行之后就能在终端打印liwenzhou.com网站首页的内容了,我们的浏览器其实就是一个发送和接收HTTP协议数据的客户端,我们平时通过浏览器访问网页其实就是从网站的服务器接收HTTP数据,然后浏览器会按照HTML、CSS等规则将网页渲染展示出来。

带参数的GET请求示例

关于GET请求的参数需要使用Go语言内置的net/url这个标准库来处理。

  1. func main() {
  2. apiUrl := "http://127.0.0.1:9090/get"
  3. // URL param
  4. data := url.Values{}
  5. data.Set("name", "小王子")
  6. data.Set("age", "18")
  7. u, err := url.ParseRequestURI(apiUrl)
  8. if err != nil {
  9. fmt.Printf("parse url requestUrl failed, err:%v\n", err)
  10. }
  11. u.RawQuery = data.Encode() // URL encode
  12. fmt.Println(u.String())
  13. resp, err := http.Get(u.String())
  14. if err != nil {
  15. fmt.Printf("post failed, err:%v\n", err)
  16. return
  17. }
  18. defer resp.Body.Close()
  19. b, err := ioutil.ReadAll(resp.Body)
  20. if err != nil {
  21. fmt.Printf("get resp failed, err:%v\n", err)
  22. return
  23. }
  24. fmt.Println(string(b))
  25. }

对应的Server端HandlerFunc如下:

  1. func getHandler(w http.ResponseWriter, r *http.Request) {
  2. defer r.Body.Close()
  3. data := r.URL.Query()
  4. fmt.Println(data.Get("name"))
  5. fmt.Println(data.Get("age"))
  6. answer := `{"status": "ok"}`
  7. w.Write([]byte(answer))
  8. }

Post请求示例

上面演示了使用net/http包发送GET请求的示例,发送POST请求的示例代码如下:

  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. "strings"
  7. )
  8. // net/http post demo
  9. func main() {
  10. url := "http://127.0.0.1:9090/post"
  11. // 表单数据
  12. //contentType := "application/x-www-form-urlencoded"
  13. //data := "name=小王子&age=18"
  14. // json
  15. contentType := "application/json"
  16. data := `{"name":"小王子","age":18}`
  17. resp, err := http.Post(url, contentType, strings.NewReader(data))
  18. if err != nil {
  19. fmt.Printf("post failed, err:%v\n", err)
  20. return
  21. }
  22. defer resp.Body.Close()
  23. b, err := ioutil.ReadAll(resp.Body)
  24. if err != nil {
  25. fmt.Printf("get resp failed, err:%v\n", err)
  26. return
  27. }
  28. fmt.Println(string(b))
  29. }

对应的Server端HandlerFunc如下:

  1. func postHandler(w http.ResponseWriter, r *http.Request) {
  2. defer r.Body.Close()
  3. // 1. 请求类型是application/x-www-form-urlencoded时解析form数据
  4. r.ParseForm()
  5. fmt.Println(r.PostForm) // 打印form数据
  6. fmt.Println(r.PostForm.Get("name"), r.PostForm.Get("age"))
  7. // 2. 请求类型是application/json时从r.Body读取数据
  8. b, err := ioutil.ReadAll(r.Body)
  9. if err != nil {
  10. fmt.Printf("read request.Body failed, err:%v\n", err)
  11. return
  12. }
  13. fmt.Println(string(b))
  14. answer := `{"status": "ok"}`
  15. w.Write([]byte(answer))
  16. }

自定义Client

要管理HTTP客户端的头域、重定向策略和其他设置,创建一个Client:

  1. client := &http.Client{
  2. CheckRedirect: redirectPolicyFunc,
  3. }
  4. resp, err := client.Get("http://example.com")
  5. // ...
  6. req, err := http.NewRequest("GET", "http://example.com", nil)
  7. // ...
  8. req.Header.Add("If-None-Match", `W/"wyzzy"`)
  9. resp, err := client.Do(req)
  10. // ...

自定义Transport

要管理代理、TLS配置、keep-alive、压缩和其他设置,创建一个Transport:

  1. tr := &http.Transport{
  2. TLSClientConfig: &tls.Config{RootCAs: pool},
  3. DisableCompression: true,
  4. }
  5. client := &http.Client{Transport: tr}
  6. resp, err := client.Get("https://example.com")

Client和Transport类型都可以安全的被多个goroutine同时使用。出于效率考虑,应该一次建立、尽量重用。

3. 服务端

默认的Server

ListenAndServe使用指定的监听地址和处理器启动一个HTTP服务端。处理器参数通常是nil,这表示采用包变量DefaultServeMux作为处理器。
Handle和HandleFunc函数可以向DefaultServeMux添加处理器。

  1. http.Handle("/foo", fooHandler)
  2. http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
  3. fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
  4. })
  5. log.Fatal(http.ListenAndServe(":8080", nil))

默认的Server示例

使用Go语言中的net/http包来编写一个简单的接收HTTP请求的Server端示例,net/http包是对net包的进一步封装,专门用来处理HTTP协议的数据。具体的代码如下:

  1. // http server
  2. func sayHello(w http.ResponseWriter, r *http.Request) {
  3. fmt.Fprintln(w, "Hello 沙河!")
  4. }
  5. func main() {
  6. http.HandleFunc("/", sayHello)
  7. err := http.ListenAndServe(":9090", nil)
  8. if err != nil {
  9. fmt.Printf("http server failed, err:%v\n", err)
  10. return
  11. }
  12. }

将上面的代码编译之后执行,打开你电脑上的浏览器在地址栏输入127.0.0.1:9090回车,此时就能够看到页面了。

自定义Server

要管理服务端的行为,可以创建一个自定义的Server:

  1. s := &http.Server{
  2. Addr: ":8080",
  3. Handler: myHandler,
  4. ReadTimeout: 10 * time.Second,
  5. WriteTimeout: 10 * time.Second,
  6. MaxHeaderBytes: 1 << 20,
  7. }
  8. log.Fatal(s.ListenAndServe())