本文整理自微信公众号文章,结合 Resty 官方文档 重新编写
原文来源:
前言
在 Go 语言中,使用标准库 net/http 发起 HTTP 请求时,代码往往比较繁琐。为了简化开发,社区诞生了许多优秀的第三方 HTTP 客户端库,其中 go-resty 以其简洁优雅的链式调用、强大的功能和出色的性能脱颖而出。
本文将介绍 Resty v3 的核心特性和使用方法,帮助你快速掌握这个高效的 HTTP 客户端库。
Resty 简介
Resty 是一个基于 Go 标准库 net/http 构建的 HTTP 和 REST 客户端库,同时支持 Server-Sent Events (SSE)。
为什么选择 Resty?
| 特性 |
说明 |
| 🔗 链式调用 |
直观优雅的 API 设计,代码可读性极高 |
| 🔄 自动序列化 |
轻松将结构体转为 JSON/XML,自动解析响应 |
| 🔁 重试机制 |
内置可定制的请求重试逻辑,增强可靠性 |
| 🔌 中间件支持 |
请求/响应中间件实现日志、认证等统一处理 |
| 🐛 调试友好 |
便捷的调试模式,输出详细请求日志 |
| 📊 请求追踪 |
支持 DNS、TCP、TLS 各阶段耗时分析 |
| 📁 文件上传/下载 |
简化 multipart 表单和文件操作 |
Resty v3 的改进
Resty v3 相比 v2 版本带来了显著提升:
- ✅ 更好的性能 - 内存效率优化,请求处理更快
- ✅ SSE 支持 - 原生支持 Server-Sent Events
- ✅ 更简洁的 API - 改进的接口设计
- ✅ 更好的错误处理 - 更清晰的错误信息
安装
1 2 3 4 5
| go get resty.dev/v3@latest
go get github.com/go-resty/resty/v2
|
注意:Resty v3 目前为 Beta 版本,生产环境可根据稳定性要求选择 v2。本文示例基于 v3 语法,v2 用户可参考 官方文档 进行适配。
快速开始
简单的 GET 请求
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
| package main
import ( "fmt" "resty.dev/v3" )
func main() { client := resty.New() defer client.Close()
resp, err := client.R(). EnableTrace(). Get("https://httpbin.org/get")
if err != nil { panic(err) }
fmt.Println("Status Code:", resp.StatusCode()) fmt.Println("Status:", resp.Status()) fmt.Println("Time:", resp.Time()) fmt.Println("Body:", resp.String())
ti := resp.Request.TraceInfo() fmt.Println("DNS Lookup:", ti.DNSLookup) fmt.Println("TCP Connection:", ti.TCPConnTime) fmt.Println("TLS Handshake:", ti.TLSHandshake) fmt.Println("Server Time:", ti.ServerTime) fmt.Println("Total Time:", ti.TotalTime) }
|
运行结果示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Status Code: 200 Status: 200 OK Time: 457.034718ms Body: { "args": {}, "headers": { "Accept-Encoding": "gzip", "Host": "httpbin.org", "User-Agent": "go-resty/3.0.0" }, "origin": "0.0.0.0", "url": "https://httpbin.org/get" }
DNS Lookup: 4.074657ms TCP Connection: 77.428048ms TLS Handshake: 299.623597ms Server Time: 75.414703ms Total Time: 457.034718ms
|
核心用法详解
1. GET 请求进阶
带查询参数的 GET 请求
1 2 3 4 5 6 7 8 9 10 11 12 13
| client := resty.New() defer client.Close()
resp, err := client.R(). SetQueryParams(map[string]string{ "page_no": "1", "limit": "20", "sort": "name", "order": "asc", }). SetHeader("Accept", "application/json"). SetAuthToken("your-auth-token"). Get("https://api.example.com/search")
|
使用 QueryString
1 2 3
| resp, err := client.R(). SetQueryString("productId=232&category=resty&source=google"). Get("https://api.example.com/product")
|
自动解析 JSON 到结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| type User struct { ID int `json:"id"` Name string `json:"name"` Email string `json:"email"` }
var user User
resp, err := client.R(). SetResult(&user). ForceContentType("application/json"). Get("https://api.example.com/user/123")
|
2. POST 请求
发送 JSON 字符串
1 2 3 4 5
| resp, err := client.R(). SetHeader("Content-Type", "application/json"). SetBody(`{"username":"testuser", "password":"testpass"}`). SetResult(&AuthSuccess{}). Post("https://api.example.com/login")
|
发送结构体
1 2 3 4 5 6 7 8 9 10 11 12 13
| type LoginRequest struct { Username string `json:"username"` Password string `json:"password"` }
resp, err := client.R(). SetBody(LoginRequest{ Username: "testuser", Password: "testpass", }). SetResult(&AuthSuccess{}). SetError(&AuthError{}). Post("https://api.example.com/login")
|
发送 Map
1 2 3 4 5 6
| resp, err := client.R(). SetBody(map[string]interface{}{ "username": "testuser", "password": "testpass", }). Post("https://api.example.com/login")
|
提示:Resty 会自动检测 Content-Type,struct 和 map 默认使用 application/json。
3. PUT / PATCH / DELETE 请求
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
| resp, err := client.R(). SetBody(Article{ Title: "Go Resty Guide", Content: "This is a comprehensive guide", Author: "Developer", }). SetAuthToken("your-token"). Put("https://api.example.com/articles/123")
resp, err := client.R(). SetBody(map[string]interface{}{ "tags": []string{"go", "http", "resty"}, }). Patch("https://api.example.com/articles/123")
resp, err := client.R(). SetAuthToken("your-token"). Delete("https://api.example.com/articles/123")
resp, err := client.R(). SetHeader("Content-Type", "application/json"). SetBody(`{"ids": [1002, 1006, 1007]}`). Delete("https://api.example.com/articles/batch")
resp, err := client.R().Head("https://api.example.com/resource") resp, err := client.R().Options("https://api.example.com/resource")
|
4. 文件上传
单文件上传
1 2 3
| resp, err := client.R(). SetFile("profile_img", "/path/to/image.png"). Post("https://api.example.com/upload")
|
多文件上传
1 2 3 4 5 6
| resp, err := client.R(). SetFiles(map[string]string{ "profile_img": "/path/to/image.png", "document": "/path/to/file.pdf", }). Post("https://api.example.com/upload")
|
带表单数据的文件上传
1 2 3 4 5 6 7 8 9 10
| resp, err := client.R(). SetFiles(map[string]string{ "avatar": "/path/to/avatar.png", }). SetFormData(map[string]string{ "first_name": "John", "last_name": "Doe", "email": "john@example.com", }). Post("https://api.example.com/profile")
|
5. 文件下载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| client := resty.New() defer client.Close()
client.SetOutputDirectory("/Users/jeeva/Downloads")
_, err := client.R(). SetOutput("plugin.zip"). Get("https://example.com/files/plugin.zip")
_, err := client.R(). SetOutput("/absolute/path/to/plugin.zip"). Get("https://example.com/files/plugin.zip")
|
6. 表单提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| resp, err := client.R(). SetFormData(map[string]string{ "username": "testuser", "password": "testpass", }). Post("https://api.example.com/login")
resp, err := client.R(). SetFormDataFromValues(url.Values{ "tags": []string{"go", "http", "resty"}, }). Post("https://api.example.com/search")
|
7. URL 路径参数
1 2 3 4 5 6 7 8
| resp, err := client.R(). SetPathParams(map[string]string{ "userId": "user@example.com", "subAccountId": "100002", }). Get("/v1/users/{userId}/{subAccountId}/details")
|
高级特性
1. 重试机制
Resty 内置强大的重试功能,支持指数退避:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| client := resty.New() defer client.Close()
client. SetRetryCount(3). SetRetryWaitTime(5 * time.Second). SetRetryMaxWaitTime(20 * time.Second). AddRetryCondition(func(r *resty.Response) (bool, error) { return r.StatusCode() == http.StatusTooManyRequests, nil })
|
2. 请求/响应中间件
1 2 3 4 5 6 7 8 9 10 11 12 13
| client.OnBeforeRequest(func(c *resty.Client, req *resty.Request) error { req.SetHeader("X-Request-ID", uuid.New().String()) return nil })
client.OnAfterResponse(func(c *resty.Client, resp *resty.Response) error { log.Printf("Response: %d %s", resp.StatusCode(), resp.Status()) return nil })
|
3. 错误处理钩子
1 2 3 4 5 6 7
| client.OnError(func(req *resty.Request, err error) { if v, ok := err.(*resty.ResponseError); ok { log.Printf("Request failed after retries: %v", v.Err) } })
|
4. 调试模式与生成 CURL 命令
1 2 3 4 5 6 7 8 9 10 11
| resp, err := client.R(). SetDebug(true). EnableGenerateCurlOnDebug(). SetBody(map[string]string{"name": "Alex"}). Post("https://httpbin.org/post")
curlCmd := resp.Request.GenerateCurlCommand() fmt.Println("Curl Command:", curlCmd)
|
5. 重定向策略
1 2 3 4 5 6 7 8 9 10 11
| client := resty.New() defer client.Close()
client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(15))
client.SetRedirectPolicy( resty.FlexibleRedirectPolicy(20), resty.DomainCheckRedirectPolicy("api.example.com", "cdn.example.com"), )
|
6. 代理设置
1 2 3 4 5 6 7 8
| client := resty.New() defer client.Close()
client.SetProxy("http://proxyserver:8888")
client.RemoveProxy()
|
7. 自定义 TLS 证书
1 2 3 4 5 6 7 8 9
| client := resty.New() defer client.Close()
client.SetRootCertificate("/path/to/root.pem")
cert, _ := tls.LoadX509KeyPair("certs/client.pem", "certs/client.key") client.SetCertificates(cert)
|
8. 自定义 JSON/XML 库
1 2 3 4 5 6 7 8
| import jsoniter "github.com/json-iterator/go"
json := jsoniter.ConfigCompatibleWithStandardLibrary
client := resty.New() client. SetJSONMarshaler(json.Marshal). SetJSONUnmarshaler(json.Unmarshal)
|
Server-Sent Events (SSE) 支持
Resty v3 原生支持 SSE,这是相比 v2 的重要新增功能:
1 2 3 4 5 6 7 8 9 10 11 12 13
| es := resty.NewEventSource(). SetURL("https://sse.dev/test"). OnMessage(func(e any) { event := e.(*resty.Event) fmt.Printf("Event: %s, Data: %s\n", event.Type, event.Data) }, nil)
err := es.Get() if err != nil { panic(err) }
|
多客户端管理
1 2 3 4 5 6 7 8 9 10 11 12 13
| client1 := resty.New() client1.SetBaseURL("https://api.service1.com") client1.R().Get("/users")
client2 := resty.New() client2.SetBaseURL("https://api.service2.com") client2.R().Get("/products")
defer client1.Close() defer client2.Close()
|
性能对比
| 特性 |
net/http |
Resty v2 |
Resty v3 |
| 代码简洁度 |
⭐⭐ |
⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
| 内存效率 |
⭐⭐⭐ |
⭐⭐⭐ |
⭐⭐⭐⭐ |
| 功能丰富度 |
⭐⭐ |
⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
| 学习曲线 |
⭐⭐ |
⭐⭐⭐⭐ |
⭐⭐⭐⭐ |
| 调试能力 |
⭐⭐ |
⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
最佳实践
- 使用
defer client.Close() - 确保释放资源
- 复用客户端实例 - 避免频繁创建销毁
- 设置合理的超时 -
client.SetTimeout(30 * time.Second)
- 使用中间件统一处理 - 日志、认证、错误处理
- 生产环境配置重试 - 提高请求可靠性
- 启用调试模式排查问题 - 开发阶段使用
SetDebug(true)
总结
Resty 通过简洁的链式 API 设计,让开发者能够专注于业务逻辑而非 HTTP 细节。无论是快速原型开发还是构建生产级应用,Resty 都是 Go 语言开发者值得信赖的 HTTP 客户端库。
Resty v3 带来的性能提升和 SSE 支持,使其更加完善。如果你的项目对稳定性要求极高,可以继续使用 v2;如果希望体验新特性和更好的性能,不妨尝试 v3。
参考资料
Happy Coding! 🚀