go 网络请求包 resty
简介
resty是 Go 语言的一个 HTTP client 库。resty功能十分强大,特性丰富,并提供了简单易用的 API。
详情请到 官方文档地址https://github.com/go-resty/resty
- 安装
// step1: 打开 go.mod文件 ==================================
// 加入一下 引用
require github.com/go-resty/resty/v2 v2.7.0
// step2: 打开main.go文件 ==================================
package main
import (
"fmt"
// 加入 指定版本的 引用
"github.com/go-resty/resty/v2"
)
func main() {
client := resty.New()
resp, err := client.R().Get("https://httpbin.org/get")
fmt.Println(" Status :", resp.Status())
}
// step3: 下载包 ==================================
go mod tidy
一般使用
客户端
调用一个resty.New() 创建一个client对象(客户端)
- 开启 debug 模式
每次请求可以 查看 请求报文 和 响应报文 的详细信息。
client := resty.New()
// Enable debug mode
client.SetDebug(true)
- 禁用 https 证书检查
client.SetTLSClientConfig(&tls.Config{ InsecureSkipVerify: true })
- 请求超时设置
client.SetTimeout(1 * time.Minute)
- URL公共部分抽离
client := resty.New()
client.SetBaseURL("https://whero.com")
resp, err := client.R().Get("/s/user")
请求方法
Get
调用client对象的R() 方法创建一个请求对象;
- Query
// Create a Resty Client
client := resty.New()
resp, err := client.R().
SetQueryParams(map[string]string{
"age": "13",
"name": "tom",
}).Get("/search_result")
- Params
client.R().
SetPathParams(map[string]string{
"age": "12",
"name": "tom",
}).
Get("/users/{age}/{name}")
- 请求体
client := resty.New()
// 让get请求携带请求体参数需要额外配置
client.SetAllowGetMethodPayload(true)
resp, err := client.R().
SetBody(`{"username":"testuser", "password":"testpass"}`).
Get("/search_result")
Post
- json
// Create a Resty Client
client := resty.New()
// POST JSON string
resp, err := client.R().
SetHeader("Content-Type", "application/json").
// 请求体
SetBody(`{"username":"testuser", "password":"testpass"}`).
Post("https:")
// POST []byte array
SetBody([]byte(`{"username":"testuser", "password":"testpass"}`))
// POST Struct, default is JSON content type. No need to set one
SetBody(User{Username: "testuser", Password: "testpass"}).
// POST Map, default is JSON content type. No need to set one
SetBody(map[string]interface{}{"username": "testuser", "password": "testpass"}).
- form-data
resp, err := client.R().
SetFormData(map[string]string{
"access_token": "BC594900-518B-4F7E-AC75-BD37F019E08F",
}).
Post("")
- x-www-form-urlencoded
Put
// Create a Resty Client
client := resty.New()
// Request goes as JSON content type
// No need to set auth token, error, if you have client level settings
resp, err := client.R().
SetBody(Article{
Title: "go-resty",
}).
Put("")
Delete
resp, err := client.R().
Delete("")
响应体
- 响应数据
resp, _ := client.R().Get("https://baidu.com")
fmt.Println("Status Code:", resp.StatusCode()) // 状态码,如 200;
fmt.Println("Status:", resp.Status()) // 状态码和状态信息,如 200 OK;
fmt.Println("Proto:", resp.Proto()) // 协议,如 HTTP/1.1;
fmt.Println("Time:", resp.Time()) // 从发送请求到收到响应的时间;
fmt.Println("Received At:", resp.ReceivedAt()) // 接收到响应的时刻;
fmt.Println("Size:", resp.Size()) // 响应大小;
fmt.Println("Headers:", resp.Header()) // 响应首部信息,以http.Header类型返回,即map[string][]string;
for key, value := range resp.Header() {
fmt.Println(key, "=", value)
}
fmt.Println("Cookies:", resp.Cookies()) // 服务器通过Set-Cookie首部设置的 cookie 信息。
for i, cookie := range resp.Cookies() {
fmt.Printf("cookie%d: name:%s value:%s\n", i, cookie.Name, cookie.Value)
}
- 自动 Unmarshal
返回结构化的数据,如 JSON/XML 格式等。resty可以自动将响应数据 Unmarshal 到对应的结构体对象中。
type Man struct {
Name string
Age int64
}
func main() {
client := resty.New()
tom := &Man{}
client.R().SetResult(tom). // 通过 SetResult 方法 将数据反射到 结构体 上。
Get("")
}
一般请求下,resty会根据响应中的Content-Type来推断数据格式。但是有时候响应中无Content-Type首部或与内容格式不一致,
我们可以通过调用请求对象的 ForceContentType() 强制
让resty按照 特定的格式
来 解析响应:
client.R().
SetResult(tom).
ForceContentType("application/json").
Get("")
请求头
- 设置一般首部
client.R().
SetHeader("Content-Type", "application/json").
SetHeader("aaa", "bbb").
Get("")
client.SetHeaders(map[string]string{
"Content-Type": "application/json",
"aaa": "bbb",
})
- Content-Length首部,resty自动计算
client.R().
SetBody(User{Name:"dj"}).
SetContentLength(true).
Get("")
上下文件
上传文件
- 通过 io.Reader
file1, _ := ioutil.ReadFile("./static/aaa.png")
file2, _ := ioutil.ReadFile("./static/bbb.png")
client := resty.New()
client.R().
SetFileReader("file_one", file1).
SetFileReader("file_two", file2).
SetFormData(map[string]string{
"name": "tom",
"age": "11",
})
Post("")
- 通过 文件路径
client := resty.New()
client.R().
SetFile("file_one", "./static/aaa.png").
SetFile("file_two", "./static/bbb.png").
SetFiles(map[string]string{
"file1": "./static/aaa.png",
"file2": "./static/bbb.png",
}).
SetFormData(map[string]string{
"name": "tom",
"age": "11",
})
Post("")
下载文件
client := resty.New()
// 如果该目录 不存在 则会 自动创建
client.SetOutDirectory("/home/hero")
// 使用相对路径, 相对SetOutDirectory 设置的路径
client.R().
SetOutput("static/aaa.png").
Get("")
// 也可以使用绝对路径
client.R().
SetOutput("/home/hero/static/aaa.png").
Get("")
高级应用
中间件
Resty 提供了和Gin类似的中间件特性。 OnBeforeRequest 和 OnAfterResponse 回调方法,可以在请求之前和响应之后加入自定义逻辑。参数包含了 resty.Client 和当前请求的 resty.Request 对象。成功时返回 nil ,失败时返回 error 对象。
client := resty.New()
client.OnBeforeRequest(func(c *resty.Client, req *resty.Request) error {
return nil
})
client.OnAfterResponse(func(c *resty.Client, resp *resty.Response) error {
return nil
})
请求重试
由于 网络抖动
带来的接口稳定性的问题 Resty 提供了重试功能来解决。SetRetryCount
设置重试次数, SetRetryWaitTime
和 SetRetryMaxWaitTime
设置等待时间。 SetRetryAfter
是一个重试后的回调方法。除此之外还可以调用 AddRetryCondition
设置重试的条件。
client := resty.New()
client.
SetRetryCount(3).
SetRetryWaitTime(5 * time.Second).
SetRetryMaxWaitTime(20 * time.Second).
SetRetryAfter(func(client *resty.Client, resp *resty.Response) (time.Duration, error) {
return 0, errors.New("quota exceeded")
})
client.AddRetryCondition(
func(r *resty.Response) (bool, error) {
return r.StatusCode() == http.StatusTooManyRequests
},
)
代理
Resty 提供了 SetProxy 方法为请求添加代理,还可以调用 RemoveProxy 移除代理。
client := resty.New()
client.SetProxy("http://proxyserver:1234")
client.RemoveProxy()
debug模式
Go1.7 引入了HTTP trace,可以在HTTP客户端请求过程中收集一些更细粒度的信息,httptrace包 提供了HTTP trace的支持,收集的信息可用于调试延迟问题,服务监控,编写自适应系统等。httptrace包提供了许多钩子,在HTTP往返期间收集各种事件的信息,包括连接的创建、复用、DNS解析查询、写入请求和读取响应。
resty提供的一个辅助功能:trace, 就是基于 httptrace包。我们在请求对象上调用EnableTrace
()方法启用 trace。启用 trace 可以记录请求的每一步的耗时和其他信息。
resp, err :=client.R().EnableTrace().Get("https://baidu.com")
ti := resp.Request.TraceInfo()
fmt.Println("DNSLookup:", ti.DNSLookup) // DNS 查询时间,如果提供的是一个域名而非 IP,就需要向 DNS 系统查询对应 IP 才能进行后续操作;
fmt.Println("ConnTime:", ti.ConnTime) // 获取一个连接的耗时,可能从连接池获取,也可能新建;
fmt.Println("TCPConnTime:", ti.TCPConnTime) // TCP 连接耗时,从 DNS 查询结束到 TCP 连接建立;
fmt.Println("TLSHandshake:", ti.TLSHandshake) // TLS 握手耗时;
fmt.Println("ServerTime:", ti.ServerTime) // 服务器处理耗时,计算从连接建立到客户端收到第一个字节的时间间隔;
fmt.Println("ResponseTime:", ti.ResponseTime) // 响应耗时,从接收到第一个响应字节,到接收到完整响应之间的时间间隔;
fmt.Println("TotalTime:", ti.TotalTime) // 整个流程的耗时;
fmt.Println("IsConnReused:", ti.IsConnReused) // TCP 连接是否复用了;
fmt.Println("IsConnWasIdle:", ti.IsConnWasIdle) // 连接是否是从空闲的连接池获取的;
fmt.Println("ConnIdleTime:", ti.ConnIdleTime) // 连接空闲时间;
fmt.Println("RequestAttempt:", ti.RequestAttempt) // 请求执行流程中的请求次数,包括重试次数;
fmt.Println("RemoteAddr:", ti.RemoteAddr.String()) // 远程的服务地址,IP:PORT格式。