简介
restyresty
快速使用
本文代码使用 Go Modules。
创建目录并初始化:
$ mkdir resty && cd resty
$ go mod init github.com/darjun/go-daily-lib/resty
resty
$ go get -u github.com/go-resty/resty/v2
下面我们来获取百度首页信息:
package main
import (
"fmt"
"log"
"github.com/go-resty/resty/v2"
)
func main() {
client := resty.New()
resp, err := client.R().Get("https://baidu.com")
if err != nil {
log.Fatal(err)
}
fmt.Println("Response Info:")
fmt.Println("Status Code:", resp.StatusCode())
fmt.Println("Status:", resp.Status())
fmt.Println("Proto:", resp.Proto())
fmt.Println("Time:", resp.Time())
fmt.Println("Received At:", resp.ReceivedAt())
fmt.Println("Size:", resp.Size())
fmt.Println("Headers:")
for key, value := range resp.Header() {
fmt.Println(key, "=", value)
}
fmt.Println("Cookies:")
for i, cookie := range resp.Cookies() {
fmt.Printf("cookie%d: name:%s value:%s\n", i, cookie.Name, cookie.Value)
}
}
resty
resty.New()clientclientR()Get()/Post()
上面程序中我们获取了:
StatusCode()Status()Proto()Time()ReceivedAt()Size()Header()http.Headermap[string][]stringCookies()Set-Cookie
运行程序输出的响应基本信息:
Response Info:
Status Code: 200
Status: 200 OK
Proto: HTTP/1.1
Time: 415.774352ms
Received At: 2021-06-26 11:42:45.307157 +0800 CST m=+0.416547795
Size: 302456
首部信息:
Headers:
Server = [BWS/1.1]
Date = [Sat, 26 Jun 2021 03:42:45 GMT]
Connection = [keep-alive]
Bdpagetype = [1]
Bdqid = [0xf5a61d240003b218]
Vary = [Accept-Encoding Accept-Encoding]
Content-Type = [text/html;charset=utf-8]
Set-Cookie = [BAIDUID=BF2EE47AAAF7A20C6971F1E897ABDD43:FG=1; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com BIDUPSID=BF2EE47AAAF7A20C6971F1E897ABDD43; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com PSTM=1624678965; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com BAIDUID=BF2EE47AAAF7A20C716E90B86906D6B0:FG=1; max-age=31536000; expires=Sun, 26-Jun-22 03:42:45 GMT; domain=.baidu.com; path=/; version=1; comment=bd BDSVRTM=0; path=/ BD_HOME=1; path=/ H_PS_PSSID=34099_31253_34133_34072_33607_34135_26350; path=/; domain=.baidu.com]
Traceid = [1624678965045126810617700867425882583576]
P3p = [CP=" OTI DSP COR IVA OUR IND COM " CP=" OTI DSP COR IVA OUR IND COM "]
X-Ua-Compatible = [IE=Edge,chrome=1]
Set-Cookie
Cookies:
cookie0: name:BAIDUID value:BF2EE47AAAF7A20C6971F1E897ABDD43:FG=1
cookie1: name:BIDUPSID value:BF2EE47AAAF7A20C6971F1E897ABDD43
cookie2: name:PSTM value:1624678965
cookie3: name:BAIDUID value:BF2EE47AAAF7A20C716E90B86906D6B0:FG=1
cookie4: name:BDSVRTM value:0
cookie5: name:BD_HOME value:1
cookie6: name:H_PS_PSSID value:34099_31253_34133_34072_33607_34135_26350
自动 Unmarshal
restyapi.cdnjs.com/libraries
resty
type Library struct {
Name string
Latest string
}
type Libraries struct {
Results []*Library
}
func main() {
client := resty.New()
libraries := &Libraries{}
client.R().SetResult(libraries).Get("https://api.cdnjs.com/libraries")
fmt.Printf("%d libraries\n", len(libraries.Results))
for _, lib := range libraries.Results {
fmt.Println("first library:")
fmt.Printf("name:%s latest:%s\n", lib.Name, lib.Latest)
break
}
}
SetResult()resty
运行:
$ go run main.go
4040 libraries
first library:
name:vue latest:https://cdnjs.cloudflare.com/ajax/libs/vue/3.1.2/vue.min.js
https://api.cdnjs.com/libraries/vue
resty
restyContent-TypeContent-TypeForceContentType()resty
client.R().
SetResult(result).
ForceContentType("application/json")
请求信息
restySetQueryString()
client.R().
SetQueryString("name=dj&age=18").
Get(...)
SetQueryParams()map[string]stringresty
client.R().
SetQueryParams(map[string]string{
"name": "dj",
"age": "18",
}).
Get(...)
restySetPathParams()map[string]stringmap
client.R().
SetPathParams(map[string]string{
"user": "dj",
}).
Get("/v1/users/{user}/details")
{}
设置首部:
client.R().
SetHeader("Content-Type", "application/json").
Get(...)
设置请求消息体:
client.R().
SetHeader("Content-Type", "application/json").
SetBody(`{"name": "dj", "age":18}`).
Get(...)
[]bytemap[string]interface{}
Content-Lengthresty
client.R().
SetBody(User{Name:"dj", Age:18}).
SetContentLength(true).
Get(...)
有些网站需要先获取 token,然后才能访问它的 API。设置 token:
client.R().
SetAuthToken("youdontknow").
Get(...)
案例
https://api.github.com
GET /orgs/{org}/repos
我们还可以设置以下这些参数:
acceptapplication/vnd.github.v3+jsonorgtypepublic/private/forks(fork的仓库)sortcreated/updated/pushed/full_namedirectionascdscper_pagepageper_page
Settings
Developer settings
Personal access tokensGenerate new token
填写 Note,表示 token 的用途,这个根据自己情况填写即可。下面复选框用于选择该 token 有哪些权限,这里不需要勾选:
Generate token
注意,这个 token 只有现在能看见,关掉页面下次再进入就无法看到了。所以要保存好,另外不要用我的 token,测试完程序后我会删除 token😭。
响应中的 JSON 格式数据如下所示:
字段非常多,为了方便起见,我这里之处理几个字段:
type Repository struct {
ID int `json:"id"`
NodeID string `json:"node_id"`
Name string `json:"name"`
FullName string `json:"full_name"`
Owner *Developer `json:"owner"`
Private bool `json:"private"`
Description string `json:"description"`
Fork bool `json:"fork"`
Language string `json:"language"`
ForksCount int `json:"forks_count"`
StargazersCount int `json:"stargazers_count"`
WatchersCount int `json:"watchers_count"`
OpenIssuesCount int `json:"open_issues_count"`
}
type Developer struct {
Login string `json:"login"`
ID int `json:"id"`
NodeID string `json:"node_id"`
AvatarURL string `json:"avatar_url"`
GravatarID string `json:"gravatar_id"`
Type string `json:"type"`
SiteAdmin bool `json:"site_admin"`
}
resty
func main() {
client := resty.New()
var result []*Repository
client.R().
SetAuthToken("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").
SetHeader("Accept", "application/vnd.github.v3+json").
SetQueryParams(map[string]string{
"per_page": "3",
"page": "1",
"sort": "created",
"direction": "asc",
}).
SetPathParams(map[string]string{
"org": "golang",
}).
SetResult(&result).
Get("https://api.github.com/orgs/{org}/repos")
for i, repo := range result {
fmt.Printf("repo%d: name:%s stars:%d forks:%d\n", i+1, repo.Name, repo.StargazersCount, repo.ForksCount)
}
}
上面程序拉取以创建时间升序排列的 3 个仓库:
$ go run main.go
repo1: name:gddo stars:1097 forks:289
repo2: name:lint stars:3892 forks:518
repo3: name:glog stars:2738 forks:775
Trace
restyrestyEnableTrace()resty
client.R().EnableTrace().Get("https://baidu.com")
TraceInfo()
ti := resp.Request.TraceInfo()
fmt.Println("Request Trace Info:")
fmt.Println("DNSLookup:", ti.DNSLookup)
fmt.Println("ConnTime:", ti.ConnTime)
fmt.Println("TCPConnTime:", ti.TCPConnTime)
fmt.Println("TLSHandshake:", ti.TLSHandshake)
fmt.Println("ServerTime:", ti.ServerTime)
fmt.Println("ResponseTime:", ti.ResponseTime)
fmt.Println("TotalTime:", ti.TotalTime)
fmt.Println("IsConnReused:", ti.IsConnReused)
fmt.Println("IsConnWasIdle:", ti.IsConnWasIdle)
fmt.Println("ConnIdleTime:", ti.ConnIdleTime)
fmt.Println("RequestAttempt:", ti.RequestAttempt)
fmt.Println("RemoteAddr:", ti.RemoteAddr.String())
我们可以获取以下信息:
DNSLookupConnTimeTCPConnTimeTLSHandshakeServerTimeResponseTimeTotalTimeIsConnReusedIsConnWasIdleConnIdleTimeRequestAttemptRemoteAddrIP:PORT
restyrestynet/http/httptracehttptrace
// src/net/http/httptrace.go
type ClientTrace struct {
GetConn func(hostPort string)
GotConn func(GotConnInfo)
PutIdleConn func(err error)
GotFirstResponseByte func()
Got100Continue func()
Got1xxResponse func(code int, header textproto.MIMEHeader) error // Go 1.11
DNSStart func(DNSStartInfo)
DNSDone func(DNSDoneInfo)
ConnectStart func(network, addr string)
ConnectDone func(network, addr string, err error)
TLSHandshakeStart func() // Go 1.8
TLSHandshakeDone func(tls.ConnectionState, error) // Go 1.8
WroteHeaderField func(key string, value []string) // Go 1.11
WroteHeaders func()
Wait100Continue func()
WroteRequest func(WroteRequestInfo)
}
resty
// src/github.com/go-resty/resty/trace.go
func (t *clientTrace) createContext(ctx context.Context) context.Context {
return httptrace.WithClientTrace(
ctx,
&httptrace.ClientTrace{
DNSStart: func(_ httptrace.DNSStartInfo) {
t.dnsStart = time.Now()
},
DNSDone: func(_ httptrace.DNSDoneInfo) {
t.dnsDone = time.Now()
},
ConnectStart: func(_, _ string) {
if t.dnsDone.IsZero() {
t.dnsDone = time.Now()
}
if t.dnsStart.IsZero() {
t.dnsStart = t.dnsDone
}
},
ConnectDone: func(net, addr string, err error) {
t.connectDone = time.Now()
},
GetConn: func(_ string) {
t.getConn = time.Now()
},
GotConn: func(ci httptrace.GotConnInfo) {
t.gotConn = time.Now()
t.gotConnInfo = ci
},
GotFirstResponseByte: func() {
t.gotFirstResponseByte = time.Now()
},
TLSHandshakeStart: func() {
t.tlsHandshakeStart = time.Now()
},
TLSHandshakeDone: func(_ tls.ConnectionState, _ error) {
t.tlsHandshakeDone = time.Now()
},
},
)
}
TraceInfo
// src/github.com/go-resty/resty/request.go
func (r *Request) TraceInfo() TraceInfo {
ct := r.clientTrace
if ct == nil {
return TraceInfo{}
}
ti := TraceInfo{
DNSLookup: ct.dnsDone.Sub(ct.dnsStart),
TLSHandshake: ct.tlsHandshakeDone.Sub(ct.tlsHandshakeStart),
ServerTime: ct.gotFirstResponseByte.Sub(ct.gotConn),
IsConnReused: ct.gotConnInfo.Reused,
IsConnWasIdle: ct.gotConnInfo.WasIdle,
ConnIdleTime: ct.gotConnInfo.IdleTime,
RequestAttempt: r.Attempt,
}
if ct.gotConnInfo.Reused {
ti.TotalTime = ct.endTime.Sub(ct.getConn)
} else {
ti.TotalTime = ct.endTime.Sub(ct.dnsStart)
}
if !ct.connectDone.IsZero() {
ti.TCPConnTime = ct.connectDone.Sub(ct.dnsDone)
}
if !ct.gotConn.IsZero() {
ti.ConnTime = ct.gotConn.Sub(ct.getConn)
}
if !ct.gotFirstResponseByte.IsZero() {
ti.ResponseTime = ct.endTime.Sub(ct.gotFirstResponseByte)
}
if ct.gotConnInfo.Conn != nil {
ti.RemoteAddr = ct.gotConnInfo.Conn.RemoteAddr()
}
return ti
}
运行输出:
$ go run main.go
Request Trace Info:
DNSLookup: 2.815171ms
ConnTime: 941.635171ms
TCPConnTime: 269.069692ms
TLSHandshake: 669.276011ms
ServerTime: 274.623991ms
ResponseTime: 112.216µs
TotalTime: 1.216276906s
IsConnReused: false
IsConnWasIdle: false
ConnIdleTime: 0s
RequestAttempt: 1
RemoteAddr: 18.235.124.214:443
我们看到 TLS 消耗了近一半的时间。
总结
resty
大家如果发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue😄
参考
我
欢迎关注我的微信公众号【GoUpUp】,共同学习,一起进步~