Go 内置的 net/http 包提供了 HTTP 客户端和服务端的实现。
https://pkg.go.dev/net/http
简介
net/http 包及其主要功能
net/http 是 Go 语言标准库中的一个重要包,用于处理 HTTP 协议相关的操作。它提供了丰富的功能,包括:
- Request,HTTP请求对象
- Response,HTTP响应对象
- Client,HTTP客户端
- Server,HTTP服务端
net/http 包中的关键组件
HTTP 客户端:
http.Get
、http.Post
等方法用于发送 HTTP 请求。
http.Client
结构体用于自定义客户端行为,如设置超时、重定向策略等。
HTTP 服务器:
http.Server
结构体表示一个HTTP服务器实例。
http.ListenAndServe
函数用于启动HTTP服务器,监听指定端口。
http.HandleFunc
和 http.Handle
函数用于注册路由和处理函数。
HTTP 客户端
简单使用
http 包提供了对应于每个 HTTP 动词的函数来发送 HTTP 请求,当你不需要对请求进行详细的定制时可以直接使用它们。
1 2 3
| resp, err := http.Get("http://baidu.com/") resp, err := http.Post("http://baidu.com/upload", "image/jpeg", &buf) resp, err := http.PostForm("http://baidu.com/form", url.Values{"key": {"Value"}, "id": {"123"}})
|
程序在使用完 response 后必须关闭回复的主体。
1 2 3 4 5 6
| resp, err := http.Get("http://baidu.com/") if err != nil { } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body)
|
HTTP请求和响应
HTTP 作为一个通信协议,通过报文传递信息;报文分为请求报文和响应报文,在 http 包中,分别用 Reqeust 和 Response 对象进行了抽象。
Request
可以通过 NewRequest 创建一个 Request 对象,方法声明如下,需要传入 HTTP 方法,URL 以及报文体进行初始化:
1
| func NewRequest(method, url string, body io.Reader) (*Request, error)
|
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
| type Request struct { Method string // HTTP方法 URL *url.URL // URL Proto string // "HTTP/1.0" ProtoMajor int // 1 ProtoMinor int // 0 Header Header // 报文头 Body io.ReadCloser // 报文体 GetBody func() (io.ReadCloser, error) ContentLength int64 // 报文长度 TransferEncoding []string // 传输编码 Close bool // 关闭连接 Host string // 主机名 Form url.Values PostForm url.Values // POST表单信息 MultipartForm *multipart.Form // multipart, Trailer Header RemoteAddr string RequestURI string TLS *tls.ConnectionState Cancel <-chan struct{} Response *Response }
|
可以看到 Request 对象可以对请求报文的各个方面进行设置,除了上述属性之外,Request 也提供了一些方法对这些属性进行访问和修改,这里不具体展开,详细文档可见Request。
Response
和 Request 对象类似,Response 也是一个数据对象,拥有多个字段来描述HTTP响应,需要注意的是 Reponse 对象拥有了当前 Request 对象的引用,对象的具体声明如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| type Response struct { Status string // HTTP 状态 "200 OK" StatusCode int // 状态码 200 Proto string // 版本号 "HTTP/1.0" ProtoMajor int // 主版本号 ProtoMinor int // 次版本号 Header Header // 响应报文头 Body io.ReadCloser // 响应报文体 ContentLength int64 // 报文长度 TransferEncoding []string // 报文编码 Close bool Trailer Header Request *Request // 请求对象 TLS *tls.ConnectionState }
|
Client
上面我们使用的 GET,POST 等函数就是通过绑定到默认 Client 实现的。我们也可以创建自己的 Client 对象,要通过 Client 发出 HTTP 请求,你需要首先初始化一个 Client 对象,然后发出请求:
1 2 3 4 5 6 7 8 9 10
| package main
import "net/http"
func main() { client := http.Client( CheckRedirect: redirectPolicyFunc, ) res, err := client.Get("http://www.google.com") }
|
对于常用 HTTP 动词,Client 对象都有对应的函数进行处理:
1 2 3 4
| func (c *Client) Get(url string) (resp *Response, err error) func (c *Client) Head(url string) (resp *Response, err error) func (c *Client) Post(url string, contentType string, body io.Reader) (resp *Response, err error) func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error)
|
但是在很多情况下,需要支持对报文头,Cookies 等的定制,上面提供的方法就不能满足需求了。所以 Client 对象还提供了一个 Do 方法,通过传入一个 Request 对象达到请求的定制化,具体方法声明如下:
1
| func (c *Client) Get(url string) (resp *Response, err error)
|
下面一个简单的配置实例:
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
| package main
import ( "net/http" "fmt" "io/ioutil" )
func main() { req, err := http.NewRequest(http.MethodGet, "http://www.baidu.com", nil) if err != nil { fmt.Println(err.Error()) return } req.Header.Set("Cookie", "name=foo") req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := http.Client{} res, err := client.Do(req) if err != nil { fmt.Println(err.Error()) return } body, err := ioutil.ReadAll(res.Body) if err != nil { fmt.Println(err.Error()) return }
fmt.Println(string(body)) }
|
package main
import (
“log”
“net/http”
“time”
)
自定义 Transport,不使用默认的
要管理代理、TLS配置、keep-alive
、压缩和其他设置,创建一个 Transport:
1 2 3 4 5 6 7 8 9 10 11
| tr := &http.Transport{ MaxIdleConns: 10, IdleConnTimeout: 30 * time.Second, MaxIdleConnsPerHost: 2, MaxConnsPerHost: 0, MaxResponseHeaderBytes: 1024 * 1024, TLSHandshakeTimeout: 10 * time.Second, DisableCompression: true, } client := &http.Client{Transport: tr} resp, err := client.Get("https://example.com")
|
在上面的代码中,我们创建了一个 http.Transport
对象,并设置了一些参数。
然后,我们使用创建的 http.Transport
对象来创建一个 http.Client
对象,作为我们进行 HTTP 请求的客户端。
最后,我们就可以使用创建的 http.Client
对象来发送HTTP请求了。在发送HTTP请求时,底层的 http.Transport
会根据我们设置的参数来处理连接的管理与控制。无需手动管理连接的建立和关闭,http.Transport
会自动进行连接的复用和闲置连接的关闭。
除了以上提到的参数,http.Transport
还提供了其他一些方法和属性,用于更精细的控制连接的管理,如 CancelRequest、CloseIdleConnections 和 ForceAttemptHTTP2。
使用 http.Transport
进行 HTTP 长连接的管理与控制,可以帮助我们提升 HTTP 请求的性能和效率。不过,需要根据实际情况合理设置参数,并进行测试和优化,才能达到最佳的效果。
服务端
http 包除了可以发送 HTTP 请求之外,也可以创建 HTTP 服务器,对外提供访问。可以通过 ListenandServe 方法创建一个 HTTP 服务。
默认的 Server
ListenAndServe 使用指定的监听地址和处理器启动一个 HTTP 服务端。处理器参数通常是 nil,这表示采用包变量 DefaultServeMux 作为处理器。
Handle 和 HandleFunc 函数可以向 DefaultServeMux 添加处理器。
1 2 3 4 5
| http.Handle("/foo", fooHandler) http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }) log.Fatal(http.ListenAndServe(":8080", nil))
|
默认的 Server 示例
使用Go语言中的net/http包来编写一个简单的接收HTTP请求的Server端示例,net/http包是对net包的进一步封装,专门用来处理HTTP协议的数据。具体的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
func sayHello(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello 枯藤!") }
func main() { http.HandleFunc("/", sayHello) err := http.ListenAndServe(":9090", nil) if err != nil { fmt.Printf("http server failed, err:%v\n", err) return } }
|
将上面的代码编译之后执行,打开你电脑上的浏览器在地址栏输入127.0.0.1:9090回车,此时就能够看到 Hello 枯藤!
自定义 Server
要管理服务端的行为,可以创建一个自定义的Server:
1 2 3 4 5 6 7 8
| s := &http.Server{ Addr: ":8080", Handler: myHandler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } log.Fatal(s.ListenAndServe())
|
HTTP方法和状态码
除了上面的内容以外,http包还定义了一系列常量用于表示HTTP动词和返回状态码,详见constants