GoLang resty http请求

安装

1
go get -u github.com/go-resty/resty/v2

基本请求

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
34
35
36
37
38
39
40
41
42
43
44
func main() {
// Create a Resty Client
client := resty.New()

resp, err := client.R().
EnableTrace().
Get("https://httpbin.org/get")
// Post()
// Put()
// Patch()
// Head()
// Option()
// Delete()

// Explore response object
fmt.Println("Response Info:")
fmt.Println(" Error :", err)
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;
fmt.Println(" Cookies :", resp.Cookies()) // 服务器通过Set-Cookie首部设置的 cookie 信息。
fmt.Println(" Body :\n", resp)
fmt.Println()

// Explore trace info
fmt.Println("Request Trace Info:")
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`格式。
}

通用

设置请求参数

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 := resty.New()

resp, err := client.R().
SetQueryParams(map[string]string{
"page_no": "1",
"limit": "20",
"sort":"name",
"order": "asc",
"random":strconv.FormatInt(time.Now().Unix(), 10),
}).
// .SetQueryString("productId=232&template=fresh-sample&cat=resty&source=google&kw=buy a lot more").
SetHeader("Accept", "application/json"). // 设置请求头
SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F").
Get("/search_result")

// 请求路径中传参
client.R().
SetPathParams(map[string]string{
"user": "ifan",
}).
Get("/users/{user}/details")
}

绑定返回信息

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() {
type JsonData struct {
Name string
Latest string
}

type Datas struct {
Results []*JsonData
}

client := resty.New()

datas := &Datas{}
client.R().SetResult(datas).
ForceContentType("application/json"). // 设置接受的返回数据类型
Get("json_data_url")
fmt.Printf("%d data len \n", len(datas.Results))

for _, data := range datas.Results {
fmt.Printf("name:%s latest:%s\n", data.Name, data.Latest)
break
}
}

设置Body参数

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
func main() {
// 字符串形式
resp, err := client.R().
SetHeader("Content-Type", "application/json").
SetBody(`{"username":"testuser", "password":"testpass"}`).
.Post("url")

resp, err := client.R().
SetHeader("Content-Type", "application/json").
SetBody([]byte(`{"username":"testuser", "password":"testpass"}`)).
Post("url")

// 使用结构体
resp, err := client.R().
SetBody(User{Username: "testuser", Password: "testpass"}).
Post("url")
// 上传文件
fileBytes, _ := ioutil.ReadFile("/Users/jeeva/mydocument.pdf")

resp, err := client.R().
SetBody(fileBytes).
SetContentLength(true). // 自动计算请求体长度
SetError(&DropboxError{}). // 设置错误返回的解析内容
Post("url")
}

上传多个文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
profileImgBytes, _ := ioutil.ReadFile("/Users/jeeva/test-img.png")
notesBytes, _ := ioutil.ReadFile("/Users/jeeva/text-file.txt")

// Create a Resty Client
client := resty.New()

resp, err := client.R().
SetFileReader("profile_img", "test-img.png", bytes.NewReader(profileImgBytes)).
SetFileReader("notes", "text-file.txt", bytes.NewReader(notesBytes)).
SetFormData(map[string]string{
"first_name": "Jeevanandam",
"last_name": "M",
}).
Post("http://myapp.com/upload")
}
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
func main() {
// Create a Resty Client
client := resty.New()

// Single file scenario
resp, err := client.R().
SetFile("profile_img", "/Users/jeeva/test-img.png").
Post("http://myapp.com/upload")

// Multiple files scenario
resp, err := client.R().
SetFiles(map[string]string{
"profile_img": "/Users/jeeva/test-img.png",
"notes": "/Users/jeeva/text-file.txt",
}).
Post("http://myapp.com/upload")

// Multipart of form fields and files
resp, err := client.R().
SetFiles(map[string]string{
"profile_img": "/Users/jeeva/test-img.png",
"notes": "/Users/jeeva/text-file.txt",
}).
SetFormData(map[string]string{
"first_name": "Jeevanandam",
"last_name": "M",
"zip_code": "00001",
"city": "my city",
"access_token": "C6A79608-782F-4ED0-A11D-BD82FAD829CD",
}).
Post("http://myapp.com/profile")
}

设置回调方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main(){
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
})

// 发生错误时
client.OnError(func(req *resty.Request, err error) {
if v, ok := err.(*resty.ResponseError); ok {

}
})
}

重定向策略

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
34
35
36
func main(){
client := resty.New()

// Assign Client Redirect Policy. Create one as per you need
client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(15))

// Wanna multiple policies such as redirect count, domain name check, etc
client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20),
resty.DomainCheckRedirectPolicy("host1.com", "host2.org", "host3.net"))

// 自定义
// Using raw func into resty.SetRedirectPolicy
client.SetRedirectPolicy(resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
// Implement your logic here

// return nil for continue redirect otherwise return error to stop/prevent redirect
return nil
}))

//---------------------------------------------------

// Using struct create more flexible redirect policy
type CustomRedirectPolicy struct {
// variables goes here
}

func (c *CustomRedirectPolicy) Apply(req *http.Request, via []*http.Request) error {
// Implement your logic here

// return nil for continue redirect otherwise return error to stop/prevent redirect
return nil
}

// Registering in resty
client.SetRedirectPolicy(CustomRedirectPolicy{/* initialize variables */})
}

自定义证书

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
34
35
36
37
38
39
40
41
func main() {
// Create a Resty Client
client := resty.New()

// Custom Root certificates, just supply .pem file.
// you can add one or more root certificates, its get appended
client.SetRootCertificate("/path/to/root/pemFile1.pem")
client.SetRootCertificate("/path/to/root/pemFile2.pem")
// ... and so on!

// Adding Client Certificates, you add one or more certificates
// Sample for creating certificate object
// Parsing public/private key pair from a pair of files. The files must contain PEM encoded data.
cert1, err := tls.LoadX509KeyPair("certs/client.pem", "certs/client.key")
if err != nil {
log.Fatalf("ERROR client certificate: %s", err)
}
// ...

// You add one or more certificates
client.SetCertificates(cert1, cert2, cert3)

// Custom Root certificates from string
// You can pass you certificates throught env variables as strings
// you can add one or more root certificates, its get appended
client.SetRootCertificateFromString("-----BEGIN CERTIFICATE-----content-----END CERTIFICATE-----")
client.SetRootCertificateFromString("-----BEGIN CERTIFICATE-----content-----END CERTIFICATE-----")
// ... and so on!

// Adding Client Certificates, you add one or more certificates
// Sample for creating certificate object
// Parsing public/private key pair from a pair of files. The files must contain PEM encoded data.
cert1, err := tls.X509KeyPair([]byte("-----BEGIN CERTIFICATE-----content-----END CERTIFICATE-----"), []byte("-----BEGIN CERTIFICATE-----content-----END CERTIFICATE-----"))
if err != nil {
log.Fatalf("ERROR client certificate: %s", err)
}
// ...

// You add one or more certificates
client.SetCertificates(cert1, cert2, cert3)
}

自定义代理

1
2
3
4
5
6
7
8
9
10
11
func main() {
// Create a Resty Client
client := resty.New()

// Setting a Proxy URL and Port
// 验证呢?
client.SetProxy("http://proxyserver:8888")

// 移除代理
client.RemoveProxy()
}

重试

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
func main() {
// Create a Resty Client
client := resty.New()

// Retries are configured per client
client.
// Set retry count to non zero to enable retries
SetRetryCount(3).
// You can override initial retry wait time.
// Default is 100 milliseconds.
SetRetryWaitTime(5 * time.Second).
// MaxWaitTime can be overridden as well.
// Default is 2 seconds.
SetRetryMaxWaitTime(20 * time.Second).
// SetRetryAfter sets callback to calculate wait time between retries.
// Default (nil) implies exponential backoff with jitter
SetRetryAfter(func(client *resty.Client, resp *resty.Response) (time.Duration, error) {
return 0, errors.New("quota exceeded")
})
// 自定义重试策略
client.AddRetryCondition(
// RetryConditionFunc type is for retry condition function
// input: non-nil Response OR request execution error
func(r *resty.Response, err error) bool {
return r.StatusCode() == http.StatusTooManyRequests
},
)
}

Allow GET request with Payload

1
2
3
4
5
6
7
8
func main() {
// Create a Resty Client
client := resty.New()

// Allow GET request with Payload. This is disabled by default.
client.SetAllowGetMethodPayload(true)

}

其他的一些参数设置

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
func main() {
client := resty.New()

// Unique settings at Client level
//--------------------------------
// Debug 模式
client.SetDebug(true)

// 设置自定义证书 Refer: http://golang.org/pkg/crypto/tls/#example_Dial
client.SetTLSClientConfig(&tls.Config{ RootCAs: roots })

// 禁用安全检查
client.SetTLSClientConfig(&tls.Config{ InsecureSkipVerify: true })

// 设置链接超时
client.SetTimeout(1 * time.Minute)


// You can override all below settings and options at request level if you want to
//--------------------------------------------------------------------------------
// 设置请求的根地址,后面的请求中可以使用相对路径
client.SetHostURL("http://httpbin.org")

// 设置所有请求的请求头
client.SetHeader("Accept", "application/json")
client.SetHeaders(map[string]string{
"Content-Type": "application/json",
"User-Agent": "My custom User Agent String",
})

// 设置所有请求的Cookie
client.SetCookie(&http.Cookie{
Name:"go-resty",
Value:"This is cookie value",
Path: "/",
Domain: "sample.com",
MaxAge: 36000,
HttpOnly: true,
Secure: false,
})
client.SetCookies(cookies)

// 设置所有请求的 query parameters
client.SetQueryParam("user_id", "00001")
client.SetQueryParams(map[string]string{ // sample of those who use this manner
"api_key": "api-key-here",
"api_secert": "api-secert",
})
client.R().SetQueryString("productId=232&template=fresh-sample&cat=resty&source=google&kw=buy a lot more")

// 所有请求的Form data,POST and PUT
client.SetFormData(map[string]string{
"access_token": "BC594900-518B-4F7E-AC75-BD37F019E08F",
})

// Basic Auth for all request
client.SetBasicAuth("myuser", "mypass")

// Bearer Auth Token for all request
client.SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F")

// 自动设置请求体的长度
client.SetContentLength(true)

// 设置所有请求的异常处理
client.SetError(&Error{}) // or resty.SetError(Error{})
}

Unix Socket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func main(){
unixSocket := "/var/run/my_socket.sock"

// Create a Go's http.Transport so we can set it in resty.
transport := http.Transport{
Dial: func(_, _ string) (net.Conn, error) {
return net.Dial("unix", unixSocket)
},
}

// Create a Resty Client
client := resty.New()

// Set the previous transport that we created, set the scheme of the communication to the
// socket and set the unixSocket as the HostURL.
client.SetTransport(&transport).SetScheme("http").SetHostURL(unixSocket)

// No need to write the host's URL on the request, just the path.
client.R().Get("/index.html")
}

模拟发送请求

1
2
3
4
5
6
7
func main() {
// Create a Resty Client
client := resty.New()

// Get the underlying HTTP Client and set it to Mock
httpmock.ActivateNonDefault(client.GetClient())
}