resty

介绍: #

RESTful API 已成为现代 Web 开发的基石,可实现客户端与服务器之间的无缝通信。在本文中,我们将探索使用 Resty(一种流行的 HTTP 客户端库)在 Go 中执行常见操作(如 GET、POST、UPDATE 和 DELETE 请求)的强大功能和简便性。我们还将学习如何在请求中传递标头,从而使我们能够自定义和增强 API 交互。

网址:https://github.com/go-resty/resty

安装 Resty: #

首先,我们需要在 Go 环境中安装 Resty。我们可以使用以下命令来安装 Resty 包:

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

GET #

发出 GET 请求: #

让我们首先研究如何使用 Resty v2 执行 GET 请求。以下代码片段演示了一个简单的 GET 请求并将响应绑定到结构体中:

package main

import (
    "fmt"
    "log"

    "github.com/go-resty/resty/v2"
)

type DevUser struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    var users []DevUser

    response, err := resty.New().R().SetResult(&users).Get("https://api.example.com/users")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("GET Response:", response.Status())
    fmt.Printf("Retrieved %d users:\n", len(users))
    for _, user := range users {
        fmt.Printf("User ID: %d, Name: %s, Email: %s\n", user.ID, user.Name, user.Email)
    }
}

复杂点的GET请求 #

// 创建Resty客户端
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),
      }).
      SetHeader("Accept", "application/json").
      SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F").
      Get("/search_result")


// 使用Request.SetQueryString方法的示例
resp, err := client.R().
      SetQueryString("productId=232&template=fresh-sample&cat=resty&source=google&kw=buy a lot more").
      SetHeader("Accept", "application/json").
      SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F").
      Get("/show_product")


// 如果需要可以强制指定响应内容类型告诉Resty将JSON响应解析为你的结构体
resp, err := client.R().
      SetResult(result).
      ForceContentType("application/json").
      Get("v2/alpine/manifests/latest")

POST #

发出 POST 请求: #

要使用 Resty v2 执行 POST 请求并将响应绑定到结构体中,我们可以使用 .SetResult() 方法。下面的示例说明了如何发送带有 JSON 有效负载的 POST 请求并将响应绑定到结构体中:

package main

import (
    "fmt"
    "log"

    "github.com/go-resty/resty/v2"
)

type DevUser struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    var createdUser DevUser

    payload := DevUser{
        Name:  "John Doe",
        Email: "johndoe@example.com",
    }

    response, err := resty.New().R().
        SetHeader("Content-Type", "application/json").
        SetBody(&payload).
        SetResult(&createdUser).
        Post("https://api.example.com/users")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("POST Response:", response.Status())
    fmt.Printf("Created User: ID: %d, Name: %s, Email: %s\n", createdUser.ID, createdUser.Name, createdUser.Email)
}

实例处理流数据 #

func Test_pedestrian2(t *testing.T) {

	client := resty.New()
	// 设置文件路径
	values := make(url.Values)
	values["files"] = []string{"E:\\镜像测试\\live.MP4"}
	resp, err := client.R().
		SetHeader("Content-Type", "application/json").
		SetHeader("model", "yolo-v8-person").
		SetFormDataFromValues(values).
		SetFormData(map[string]string{
			"mode":            "intervalFrame",
			"startTime":       "2448000",
			"endTime":         "5648000",
			"intervalSeconds": "1000",
			"diff":            "2.5",
		}).
		SetDoNotParseResponse(true).
		Post("http://127.0.0.1:9120/api/v1/detection/video")
	if err != nil {
		t.Fatalf("Request failed: %v", err)
	}
	defer resp.RawBody().Close()
	decoder := json.NewDecoder(resp.RawBody())
	result := new(param.DetectionResult)

	for {
		// 按照 JSON 对象进行解码
		if err := decoder.Decode(result); err != nil {
			if err.Error() == "EOF" {
				break // 读取完毕
			}
			fmt.Println("Decode error:", err)
			break
		}

		// 处理解析结果
		fmt.Println(result.File, "   ", result.StartTime, "   ", result.EndTime)
	}
	//scanner := bufio.NewScanner(resp.RawBody())
	//for scanner.Scan() {
	//	// 逐行读取
	//	line := scanner.Text() // 获取当前行的文本
	//	fmt.Println("Received line:", line)
	//}
	// 逐块读取响应内容
	//buf := make([]byte, 1024)
	//for {
	//	n, err := resp.RawBody().Read(buf)
	//	if n > 0 {
	//		fmt.Println(string(buf[:n])) // 输出读取到的内容
	//	}
	//	if err != nil {
	//		if err.Error() != "EOF" {
	//			fmt.Println("Read error:", err)
	//		}
	//		break
	//	}
	//}
}
if resp.StatusCode() != http.StatusOK {
  err = fmt.Errorf("Unexpected status code: %d, status: %s\n", resp.StatusCode(),resp.Status())
  return
}
type result struct {
	
  Data []param.OcrResult `json:"data"`  Err  string            `json:"err"`}

data := new(result)
err = json.Unmarshal(resp.Body(), data)
if err != nil {

  fmt.Println("json unmarshal err:", err)

  common.Log.Error()	
	return
}

多种POST请求示例 #

// 创建Resty客户端
client := resty.New()

// POST JSON字符串
// 如果已经设置了客户端级别的设置,则无需设置内容类型
resp, err := client.R().
      SetHeader("Content-Type", "application/json").
      SetBody(`{"username":"testuser", "password":"testpass"}`).
      SetResult(&AuthSuccess{}).    // 或者 SetResult(AuthSuccess{}).
      Post("https://myapp.com/login")

// POST []byte字节数组
// 如果已经设置了客户端级别的设置则无需设置内容类型
resp, err := client.R().
      SetHeader("Content-Type", "application/json").
      SetBody([]byte(`{"username":"testuser", "password":"testpass"}`)).
      SetResult(&AuthSuccess{}).    // 或者 SetResult(AuthSuccess{}).
      Post("https://myapp.com/login")

// POST结构体默认是JSON内容类型无需设置
resp, err := client.R().
      SetBody(User{Username: "testuser", Password: "testpass"}).
      SetResult(&AuthSuccess{}).    // 或者 SetResult(AuthSuccess{}).
      SetError(&AuthError{}).       // 或者 SetError(AuthError{}).
      Post("https://myapp.com/login")

// POST Map默认是JSON内容类型无需设置
resp, err := client.R().
      SetBody(map[string]interface{}{"username": "testuser", "password": "testpass"}).
      SetResult(&AuthSuccess{}).    // 或者 SetResult(AuthSuccess{}).
      SetError(&AuthError{}).       // 或者 SetError(AuthError{}).
      Post("https://myapp.com/login")

// 以原始字节数组形式进行文件上传例如将文件上传到Dropbox
fileBytes, _ := os.ReadFile("/Users/jeeva/mydocument.pdf")

// 注意我们没有设置内容类型头因为go-resty会自动检测Content-Type
resp, err := client.R().
      SetBody(fileBytes).
      SetContentLength(true).          // Dropbox需要这个值
      SetAuthToken("").
      SetError(&DropboxError{}).       // 或者 SetError(DropboxError{}).
      Post("https://content.dropboxapi.com/1/files_put/auto/resty/mydocument.pdf") // DropBox也支持PUT方式上传

// 注意如果没有设置内容类型头resty会检测请求体的Content-Type
//   * 对于结构体和map数据类型默认为'application/json'
//   * 然后是普通文本内容类型

同一参数名,多个参数值 #

values := make(url.Values)

values["files"] = append(values["files"], "E:\\镜像测试\\1.jpg")
values["files"] = append(values["files"], "E:\\镜像测试\\2.jpg")
client := resty.New()

resp, err := client.R().

	SetContext(ctx).

	SetHeader("Content-Type", "application/json").

	SetHeader("model", "ch-pt-ocr-v4").
	
	SetFormDataFromValues(values).
	
	Post("http://127.0.0.1:9120" + videoOCRUrl)

	//Post(d.AiEngineHost + videoDetectionUrl)


if err != nil {

	err = fmt.Errorf("Request failed: %v", err)
	
	return

}
defer resp.RawBody().Close()

PUT #

发出 UPDATE(PUT)请求: #

要使用 Resty v2 执行更新操作并将响应绑定到结构体中,我们可以使用 .SetResult() 方法。以下示例演示如何发送带有 JSON 有效负载的 PUT 请求并将响应绑定到结构体中:

package main

import (
    "fmt"
    "log"

    "github.com/go-resty/resty/v2"
)

type DevUser struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    var updatedUser DevUser

    payload := DevUser{
        Name:  "Updated Name",
        Email: "updated@example.com",
    }

    response, err := resty.New().R().
        SetHeader("Content-Type", "application/json").
        SetBody(&payload).
        SetResult(&updatedUser).
        Put("https://api.example.com/users/123")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("PUT Response:", response.Status())
    fmt.Printf("Updated User: ID: %d, Name: %s, Email: %s\n", updatedUser.ID, updatedUser.Name, updatedUser.Email)
}

您可以像演示 POST 一样使用各种 PUT 方法调用的组合。

// 注意:这是使用PUT方法的一个示例,更多组合请参阅POST

// 创建一个Resty客户端
client := resty.New()

// 请求以JSON内容类型发送
// 如果有客户端级别的设置,可以不设置身份验证令牌,错误
resp, err := client.R().
      SetBody(Article{
        Title: "go-resty",
        Content: "这是我的文章内容,哦耶!",
        Author: "Jeevanandam M",
        Tags: []string{"文章", "示例", "resty"},
      }).
      SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
      SetError(&错误{}).       // 或者 SetError(Error{}).
      Put("https://myapp.com/article/1234")

DELETE #

发出删除请求: #

要使用 Resty v2 发送 DELETE 请求,我们可以使用 .Delete() 方法。以下是演示删除用户的示例:

package main

import (
    "fmt"
    "log"

    "github.com/go-resty/resty/v2"
)

type DevUser struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    response, err := resty.New().R().Delete("https://api.example.com/users/123")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("DELETE Response:", response.Status())
}
// 创建一个Resty客户端
client := resty.New()

// 删除一篇文章
// 如果有客户端级别的设置,可以不设置身份验证令牌,错误
resp, err := client.R().
      SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
      SetError(&错误{}).       // 或者 SetError(Error{}).
      Delete("https://myapp.com/articles/1234")

// 以JSON字符串作为有效载荷/内容的方式删除多篇文章
// 如果有客户端级别的设置,可以不设置身份验证令牌,错误
resp, err := client.R().
      SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
      SetError(&错误{}).       // 或者 SetError(Error{}).
      SetHeader("Content-Type", "application/json").
      SetBody(`{article_ids: [1002, 1006, 1007, 87683, 45432] }`).
      Delete("https://myapp.com/articles")

// 获取资源的头信息
// 如果有客户端级别的设置,可以不设置身份验证令牌
resp, err := client.R().
      SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
      Head("https://myapp.com/videos/hi-res-video")

// 获取资源的选项信息
// 如果有客户端级别的设置,可以不设置身份验证令牌
resp, err := client.R().
      SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
      Options("https://myapp.com/servers/nyc-dc-01")

传递标头: #

Resty v2 允许我们在请求中包含自定义标头。以下代码片段演示了如何使用 Resty v2 传递标头:

package main

import (
    "fmt"
    "log"

    "github.com/go-resty/resty/v2"
)

type DevUser struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    client := resty.New()
    client.SetHeader("Authorization", "Bearer YOUR_TOKEN")

    response, err := client.R().Get("https://api.example.com/protected-resource")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("GET with Headers Response:", response.Status())
}

设置JSON和XML序列化/反序列化操作 #

用户可以将选择的JSON/XML库注册到resty中,或者编写自己的库。默认情况下,resty分别注册标准的 encoding/jsonencoding/xml

// 注册json-iterator的示例
import jsoniter "github.com/json-iterator/go"

json := jsoniter.ConfigCompatibleWithStandardLibrary

client := resty.New().
    SetJSONMarshaler(json.Marshal).
    SetJSONUnmarshaler(json.Unmarshal)

// 类似地,用户可以使用以下方式对XML进行设置 -
client.SetXMLMarshaler(xml.Marshal).
    SetXMLUnmarshaler(xml.Unmarshal)

获取响应状态码 #

fmt.Println(resp.StatusCode())

获取响应头部信息 #

fmt.Println(resp.Header())

获取响应体数据 #

fmt.Println(resp.Body())

解析 JSON 响应数据 #

var result map[string]interface{}
err = json.Unmarshal(resp.Body(), &result)
if err != nil {
    fmt.Println(err)
    return
}
fmt.Println(result)

高级用法 #

除了基本的请求和响应处理,Go Resty 还提供了一些高级用法,例如连接池、超时设置、重试机制、代理等。下面是一些常见的高级用法示例:

连接池 #

client := resty.New().
    SetTransport(&http.Transport{
        MaxIdleConnsPerHost: 10,
    })

超时设置 #

client := resty.New().
    SetTimeout(10 * time.Second)

重试机制 #

client := resty.New().
    SetRetryCount(3).
    SetRetryWaitTime(5 * time.Second)

代理 #

client := resty.New().
    SetProxy("http://proxy.example.com:8080")

结论: #

在本综合指南中,我们探讨了如何利用 Resty v2(一个易于使用的 HTTP 客户端库)在 Go 中执行 GET、POST、UPDATE 和 DELETE 请求。我们还学习了如何传递标头以增强 API 交互,从而提供更高的自定义性和安全性。此外,我们还发现了如何将 API 响应绑定到 Go 结构中,从而轻松处理和操作数据。Resty v2 简化了 RESTful API 的使用,使我们能够专注于构建强大而高效的应用程序。

记得导入 Resty v2 包(github.com/go-resty/resty/v2),有效处理错误,并调整示例以适合您的特定 API 端点和要求。