net.http

Go 语言提供了 net/http 包,协助用户实施与 HTTP 协议相关的开发任务。该包既提供了 HTTP 服务器 实现,又提供了 客户端 实现。这里记录使用 Go发起 http 请求。

net/http:https://studygolang.com/static/pkgdoc/pkg/net_http.htm

http.Response

Response 是 HTTP 请求后的响应,下面是我们可以从中获取到的内容:

image-20230819104523522

读取 Body 信息,需要注意的是 :对 Body 的处理需要在其未关闭完之前进行

1
2
3
4
5
6
7
8
9
10
11
12
func GetReqBody(url string) (string, error) {
resp, err := http.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), nil
}

Header 结构体方法:

image-20230819110642269

Get 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
url := "https://httpbin.org/get"
resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
}

Post 请求

Post 请求可以使用 http.Post 或者 http.PostForm 方法,使用方法如下:

http.Post 方法:

1
func Post(url string, bodyType string, body io.Reader) (resp Response, err error)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main() {
url := "https://httpbin.org/post"
payload := []byte(`{"key": "value"}`)

resp, err := http.Post(url, "application/json", bytes.NewBuffer(payload))
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}

fmt.Println(string(body))
}

http.PostForm 方法:

1
func PostForm(url string, data url.Values) (resp Response, err error)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
targetUrl := "https://httpbin.org/post"
values := url.Values{
"key1": {"value1"},
"key2": {"value2"},
}
resp, err := http.PostForm(targetUrl, values)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}

fmt.Println(string(body))
}

HTTP Client

struct

1
2
3
4
5
6
7
8
9
10
type Client struct {
// Transport指定执行独立、单次HTTP请求的机制。
Transport RoundTripper
// 重定向
CheckRedirect func(req Request, via []Request) error
// Jar指定cookie管理器, 如果 Jar 为nil,请求中不会发送cookie,回复中的cookie会被忽略。
Jar CookieJar
// 超时
Timeout time.Duration
}

Client 的 Transport 字段一般会含有内部状态(缓存 TCP 连接),因此 Client 类型值应尽量被重用而不是每次需要都创建新的。

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

Transport

struct

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
type Transport struct {
// Proxy指定一个对给定请求返回代理的函数。
// 如果该函数返回了非nil的错误值,请求的执行就会中断并返回该错误。
// 如果Proxy为nil或返回nil的URL置,将不使用代理。
Proxy func(Request) (url.URL, error)
// Dial指定创建TCP连接的拨号函数。如果Dial为nil,会使用net.Dial。
Dial func(network, addr string) (net.Conn, error)
// TLSClientConfig指定用于tls.Client的TLS配置信息。
// 如果该字段为nil,会使用默认的配置信息。
TLSClientConfig tls.Config
// TLSHandshakeTimeout指定等待TLS握手完成的最长时间。零值表示不设置超时。
TLSHandshakeTimeout time.Duration
// 如果DisableKeepAlives为真,会禁止不同HTTP请求之间TCP连接的重用。
DisableKeepAlives bool
// 如果DisableCompression为真,会禁止Transport在请求中没有Accept-Encoding头时,
// 主动添加"Accept-Encoding: gzip"头,以获取压缩数据。
// 如果Transport自己请求gzip并得到了压缩后的回复,它会主动解压缩回复的主体。
// 但如果用户显式的请求gzip压缩数据,Transport是不会主动解压缩的。
DisableCompression bool
// 如果MaxIdleConnsPerHost!=0,会控制每个主机下的最大闲置连接。
// 如果MaxIdleConnsPerHost==0,会使用DefaultMaxIdleConnsPerHost。
MaxIdleConnsPerHost int
// ResponseHeaderTimeout指定在发送完请求(包括其可能的主体)之后,
// 等待接收服务端的回复的头域的最大时间。零值表示不设置超时。
// 该时间不包括获取回复主体的时间。
ResponseHeaderTimeout time.Duration
// 内含隐藏或非导出字段
}

Proxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func main() {
targetUrl := "https://example.com"

tr := &http.Transport{
Proxy: func(req http.Request) (url.URL, error) {
return url.Parse("http://your-proxy-server:port")
},
}

client := &http.Client{
Transport: tr,
}

resp, err := client.Get(targetUrl)
if err != nil {
panic(err)
}
defer resp.Body.Close()

fmt.Println("Response Status:", resp.Status)
}

TLSClientConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func main() {
url := "https://example.com"

tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true, // 跳过证书验证
},
}

client := &http.Client{
Transport: tr,
}

resp, err := client.Get(url)
if err != nil {
panic(err)
}
defer resp.Body.Close()

fmt.Println("Response Status:", resp.Status)
}

Timeout

http.Get 和 http.Post 都是没有设置超时的,在生产业务需要设置超时时间,以免 goroutine 被超时请求阻塞而停止响应。

1
2
3
4
5
var client = &http.Client{
Timeout: time.Second 5,
}

rsps, err := client.Get("http://example.com")

CheckRedirect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func main() {
// 重定向 3 次
CheckRedirect := func(req http.Request, via []http.Request) error {
if len(via) >= 3 {
return errors.New("stopped after 3 redirects")
}
return nil
}
client := &http.Client{
CheckRedirect: CheckRedirect,
Timeout: 15 time.Second,
}
resp, err := client.Get("https://httpbin.org/get")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
}

http.NewRequest

http.NewRequest 是新建一个请求,然后通过 client.Do 发起请求,对于 Get、Post 来说有更多的操作空间,如自定义 Header、Cookie 等操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func main() {
client := &http.Client{
Timeout: 15 time.Second,
}
request, err := http.NewRequest("GET", "https://httpbin.org/get", nil)
if err != nil {
log.Fatal(err)
}
request.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36")
request.AddCookie(&http.Cookie{
Name: "name",
Value: "fuyoumingyan",
})
response, err := client.Do(request)
if err != nil {
log.Fatal(err)
}
body, err := io.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(body))
}

image-20230819125808184


net.http
https://liancccc.github.io/2023/09/15/技术/开发/GoHttpNet/
作者
守心
发布于
2023年9月15日
许可协议