目录
官方文档 DOC: https://pkg.go.dev/github.com/levigross/grequests Github: http://github.com/levigross/grequests
Python中的Requests
库非常强大,所以Go开发者模仿Python的Requests
库,由此诞生了Grequests
库。Grequests
提供了一系列便利功能,使得发送HTTP请求变得简单高效。下面就是Grequests
在Golang中实现的一些关键特性:
Grequests
支持将HTTP响应内容序列化为JSON和XML格式,让处理API响应时更为方便。要开始使用Grequests库,你需要先在你的Go环境中安装它。通过下面的命令即可完成安装:
go get -u github.com/levigross/grequests
在安装完Grequests后,你可以通过import语句把它引入到你的Go代码中:
import "github.com/levigross/grequests"
下面是一个发送GET请求的示例,其中演示了如何获取HTTP响应并打印出来:
func Get() {
resp, err := grequests.Get("http://127.0.0.1:8080/book/", nil)
if err != nil {
log.Fatalln("Unable to make request: ", err)
}
if !resp.Ok {
log.Fatalln("请求超时!")
}
// 解析响应的JSON数据
var data []map[string]interface{}
if err := resp.JSON(&data); err != nil {
log.Fatalln("Unable to parse JSON response: ", err)
}
fmt.Println(data)
}
上面的代码首先使用Get
方法发送GET请求,然后检查是否有错误发生。如果没有错误,就可以通过resp.Json()
方法获取响应的文本内容。
在下面的例子中,我们创建了一个map对象来保存我们想要发送的JSON数据。然后我们通过ROption
创建了一个请求选项对象,并在其中指定了JSON为发送的数据类型。最后,我们调用Post
方法来发送请求:
func Post() {
postData := map[string]string{
"id": "1",
"name": "Go入门到进阶",
}
geq := &grequests.RequestOptions{
JSON: postData,
}
resp, err := grequests.Post("http://127.0.0.1:8080/book/create", geq)
if err != nil {
log.Fatalln("Unable to make request: ", err)
}
fmt.Println(resp.String())
}
下面是代码的逐行解释:
postData := map[string]string{"id": "1", "name": "Go入门到进阶"}
map[string]string
类型的变量postData
,其中包含了两个键值对,分别是"id"和"name",它们的值分别是"1"和"Go入门到进阶"。geq := &grequests.RequestOptions{JSON: postData}
grequests.RequestOptions
类型的变量geq
。grequests.RequestOptions
是一个结构体,用于配置HTTP请求的各种选项,如URL、方法、头信息、数据等。在这个例子中,我们通过JSON
字段将postData
作为JSON数据传递给POST请求。resp, err := grequests.Post("http://127.0.0.1:8080/book/create", geq)
grequests.Post
函数发起一个POST请求。http://127.0.0.1:8080/book/create
是请求的目标URL,而geq
是请求的配置选项。grequests.Post
函数会返回一个Response
对象和一个可能的错误。if err != nil { log.Fatalln("Unable to make request: ", err) }
grequests.Post
函数调用时发生错误,这个条件块会执行。log.Fatalln
函数会打印错误消息并退出程序。fmt.Println(resp.String())
resp.String()
方法会返回响应体的字符串表示,然后使用fmt.Println
函数将其打印到标准输出。
总的来说,这段代码的作用是向本地服务器(假设在127.0.0.1:8080
上)的/book/create
路径发送一个POST请求,请求体是JSON格式的数据,包含一个ID和书名。如果请求成功,它会打印出服务器的响应。如果请求失败,它会打印出错误信息并退出程序。文件上传同样简单。你可以通过RequestOptions
指定文件:
func UploadFile() {
// 允许您通过指定磁盘上的位置来创建FileUpload结构片
// 打开要上传的文件
file, err := os.Open("./go.mod")
if err != nil {
log.Fatalln("Unable to open file: ", err)
}
defer file.Close()
// 创建FileUpload结构片
ro := &grequests.RequestOptions{
Files: []grequests.FileUpload{{
FileName: "1.txt", // 上传后的文件名称
FieldName: "file", // 上传文件对应字段
FileContents: file, // 使用文件内容作为FileContents
}},
}
// 发送POST请求
resp, err := grequests.Post("http://127.0.0.1:8080/book/upload/", ro)
if err != nil {
log.Fatalln("Unable to make request: ", err)
}
fmt.Println(resp.String())
}
在上述代码中,我们创建了一个FileUpload
结构,通过FileName
、FieldName
和FileContents
来指定我们要上传的文件详情。
gorequest
代理,下面是一个简单的例子,需要把Proxies
中的URL添加为*url.URL
代理:
func Proxy() {
// 代理服务器
const proxyServer = "http-pro.xxx.com:9010"
// 代理隧道验证信息
const proxyUser = "xxxxxxxxx"
const proxyPass = "xxxxxxxxx"
// 初始化代理URL
proxyUrl, _ := url.Parse("http://" + proxyUser + ":" + proxyPass + "@" + proxyServer)
// 创建请求选项
ro := &grequests.RequestOptions{
Proxies: map[string]*url.URL{
"http": proxyUrl,
},
Headers: map[string]string{
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
},
}
// 发起GET请求
resp, err := grequests.Get("http://www.example.com", ro)
if err != nil {
fmt.Println("Error:", err)
return
}
// 打印响应状态码
fmt.Println("Status code:", resp.StatusCode)
// 打印响应体
fmt.Println("Response:", resp.String())
}
下面是代码的逐行解释:
// 代理服务器
const proxyServer = "http-pro.xxx.com:9010"
proxyServer
,它的值是代理服务器的URL,格式为http://host:port
。// 代理隧道验证信息
const proxyUser = "xxxxxxxxx"
proxyUser
,它的值是代理隧道的用户名。const proxyPass = "xxxxxxxxx"
proxyPass
,它的值是代理隧道的密码。// 初始化代理URL
proxyUrl, _ := url.Parse("http://" + proxyUser + ":" + proxyPass + "@" + proxyServer)
url.Parse
函数创建了一个代理URL。它将代理隧道的用户名、密码和代理服务器地址组合成一个URL,格式为http://username:password@host:port
。_
是忽略返回值的约定,因为返回值通常不需要使用。// 创建请求选项
grequests.RequestOptions
结构体,用于配置HTTP请求。ro := &grequests.RequestOptions{
grequests.RequestOptions
结构体变量ro
。Proxies: map[string]*url.URL{
Proxies
字段,它是一个映射,将协议(如"http")映射到代理URL。"http": proxyUrl,
},
Headers: map[string]string{
Headers
字段,它是一个映射,将HTTP头字段(如"user-agent")映射到相应的值。"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
},
}
grequests.RequestOptions
结构体变量的定义结束。// 发起GET请求
resp, err := grequests.Get("http://www.example.com", ro)
grequests.Get
函数发起一个GET请求。http://www.example.com
是请求的目标URL,而ro
是请求的配置选项。grequests.Get
函数会返回一个Response
对象和一个可能的错误。if err != nil {
grequests.Get
函数调用时发生错误,这个条件块会执行。fmt.Println("Error:", err)
return
}
fmt.Println("Status code:", resp.StatusCode)
fmt.Println("Response:", resp.String())
下面是使用Session的一个例子:
session := grequests.Session{
RequestOptions: &grequests.RequestOptions{
Headers: map[string]string{
"authority": "mp3.haoge500.com",
"referer": "https://www.zz123.com/",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
},
},
}
package main
import (
"encoding/json"
"github.com/gin-gonic/gin"
"net/http"
"os"
)
type Book struct {
ID string `json:"id"`
Name string `json:"name"`
}
type BookHandler struct {
}
func (b *BookHandler) RegisterRoutes(server *gin.Engine) {
bg := server.Group("/book")
bg.POST("/upload", b.Upload)
bg.POST("/create", b.Create)
bg.GET("/", b.GetAllBooks) // 查询书籍
}
func (b *BookHandler) Upload(ctx *gin.Context) {
// 从请求中获取文件
file, err := ctx.FormFile("file")
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "无法获取上传的文件"})
return
}
// 将文件保存到服务器
// 注意:这里需要确保保存文件的目录存在,并且服务器有写入权限
savePath := "./uploads/" + file.Filename
if err := ctx.SaveUploadedFile(file, savePath); err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "文件保存失败"})
return
}
ctx.JSON(http.StatusOK, gin.H{"message": "文件上传成功"})
}
func (b *BookHandler) Create(ctx *gin.Context) {
var req Book
if err := ctx.Bind(&req); err != nil {
return
}
// 将新的书籍数据保存到data.json文件中
if err := addBookToFile(&req); err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save book data"})
return
}
ctx.JSON(http.StatusOK, gin.H{"message": "Book added successfully"})
}
func (b *BookHandler) GetAllBooks(c *gin.Context) {
// 从data.json文件中读取书籍数据
books, err := getBooksFromFile()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read book data"})
return
}
// 获取URL查询参数中的id
id := c.Query("id")
// 如果提供了ID,查找具有匹配ID的书籍列表
if id != "" {
// 查找具有匹配ID的书籍
var foundBooks []Book
for _, book := range books {
if book.ID == id {
foundBooks = append(foundBooks, book)
}
}
// 如果找到了匹配的书籍,返回这些书籍
if len(foundBooks) > 0 {
c.JSON(http.StatusOK, foundBooks)
return
}
// 如果没有找到匹配的书籍,返回404
c.JSON(http.StatusNotFound, gin.H{"error": "Books not found"})
return
}
// 如果没有提供ID,返回所有书籍
c.JSON(http.StatusOK, books)
}
func addBookToFile(book *Book) error {
// 读取现有的data.json文件内容
var books []Book
data, err := os.ReadFile("data.json")
if err != nil && !os.IsNotExist(err) {
return err
}
// 如果文件存在,解析现有的书籍数据
if err == nil {
if err := json.Unmarshal(data, &books); err != nil {
return err
}
}
// 将新的书籍添加到数组中
books = append(books, *book)
// 将更新后的书籍数组序列化为JSON
newData, err := json.MarshalIndent(books, "", " ")
if err != nil {
return err
}
// 将序列化的JSON数据写入data.json文件
if err := os.WriteFile("data.json", newData, 0644); err != nil {
return err
}
return nil
}
func getBooksFromFile() ([]Book, error) {
// 读取data.json文件内容
data, err := os.ReadFile("data.json")
if err != nil {
return nil, err
}
// 解析JSON数据到书籍数组中
var books []Book
if err := json.Unmarshal(data, &books); err != nil {
return nil, err
}
return books, nil
}
func InitWebServer(bookHandler *BookHandler) *gin.Engine {
server := gin.Default()
bookHandler.RegisterRoutes(server)
return server
}
func main() {
// 确保上传目录存在
os.MkdirAll("./uploads", 0755)
bookHandler := &BookHandler{}
server := InitWebServer(bookHandler)
server.Run(":8080") // 在8080端口启动服务器
}