Go 标准库之 net.http

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 包中的关键组件

  1. HTTP 客户端‌:

    http.Gethttp.Post 等方法用于发送 HTTP 请求。

    http.Client 结构体用于自定义客户端行为,如设置超时、重定向策略等。

  2. HTTP 服务器‌:

    http.Server 结构体表示一个HTTP服务器实例。

    http.ListenAndServe 函数用于启动HTTP服务器,监听指定端口。

    http.HandleFunchttp.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 {
// handle error
}
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
}

// defer res.Body.Close()
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, // 每个host的最大空闲连接数
MaxConnsPerHost: 0, // 每个host的最大连接数,0表示不限制
MaxResponseHeaderBytes: 1024 * 1024, // 响应头的最大长度
TLSHandshakeTimeout: 10 * time.Second, // TLS握手的超时时间
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
// http server

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


Go 标准库之 net.http
https://flepeng.github.io/021-Go-32-Go-标准库-Go-标准库之-net-http/
作者
Lepeng
发布于
2024年12月3日
许可协议