Go 標準庫之 GoRequests 介紹與基本使用

贾维斯Echo發表於2024-04-15

目錄
  • 一、介紹
  • 二、安裝
  • 三、匯入
  • 四、基本使用
    • 4.1 傳送GET 請求
    • 4.2 POST請求傳送JSON資料
    • 4.3 Post 檔案上傳
    • 4.4 GoRequests 使用代理
    • 4.5 Gorequests 使用session
  • 五、HTTP服務端程式碼

一、介紹

官方文件 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響應時更為方便。
  • 檔案上傳和下載:提供了便捷的方式來上傳和下載檔案,無需複雜的配置。
  • HTTP動詞支援:支援廣泛的HTTP動詞,包括GET、HEAD、POST、PUT、DELETE、PATCH以及OPTIONS,可以覆蓋大多數HTTP請求場景。

二、安裝

要開始使用Grequests庫,你需要先在你的Go環境中安裝它。透過下面的命令即可完成安裝:

go get -u github.com/levigross/grequests

三、匯入

在安裝完Grequests後,你可以透過import語句把它引入到你的Go程式碼中:

import "github.com/levigross/grequests"

四、基本使用

4.1 傳送GET 請求

下面是一個傳送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()方法獲取響應的文字內容。

4.2 POST請求傳送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())
}

下面是程式碼的逐行解釋:

  1. postData := map[string]string{"id": "1", "name": "Go入門到進階"}
    • 這裡定義了一個map[string]string型別的變數postData,其中包含了兩個鍵值對,分別是"id"和"name",它們的值分別是"1"和"Go入門到進階"。
  2. geq := &grequests.RequestOptions{JSON: postData}
    • 這裡建立了一個grequests.RequestOptions型別的變數geqgrequests.RequestOptions是一個結構體,用於配置HTTP請求的各種選項,如URL、方法、頭資訊、資料等。在這個例子中,我們透過JSON欄位將postData作為JSON資料傳遞給POST請求。
  3. 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物件和一個可能的錯誤。
  4. if err != nil { log.Fatalln("Unable to make request: ", err) }
    • 如果grequests.Post函式呼叫時發生錯誤,這個條件塊會執行。log.Fatalln函式會列印錯誤訊息並退出程式。
  5. fmt.Println(resp.String())
    • 如果請求成功,這個條件塊會執行。resp.String()方法會返回響應體的字串表示,然後使用fmt.Println函式將其列印到標準輸出。
      總的來說,這段程式碼的作用是向本地伺服器(假設在127.0.0.1:8080上)的/book/create路徑傳送一個POST請求,請求體是JSON格式的資料,包含一個ID和書名。如果請求成功,它會列印出伺服器的響應。如果請求失敗,它會列印出錯誤資訊並退出程式。

4.3 Post 檔案上傳

檔案上傳同樣簡單。你可以透過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結構,透過FileNameFieldNameFileContents來指定我們要上傳的檔案詳情。

4.4 GoRequests 使用代理

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())
}

下面是程式碼的逐行解釋:

  1. // 代理伺服器
    • 這一行是一個註釋,宣告瞭接下來的程式碼將定義代理伺服器的URL。
  2. const proxyServer = "http-pro.xxx.com:9010"
    • 這裡定義了一個常量proxyServer,它的值是代理伺服器的URL,格式為http://host:port
  3. // 代理隧道驗證資訊
    • 這一行是一個註釋,宣告瞭接下來的程式碼將定義代理隧道的驗證資訊。
  4. const proxyUser = "xxxxxxxxx"
    • 這裡定義了一個常量proxyUser,它的值是代理隧道的使用者名稱。
  5. const proxyPass = "xxxxxxxxx"
    • 這裡定義了一個常量proxyPass,它的值是代理隧道的密碼。
  6. // 初始化代理URL
    • 這一行是一個註釋,說明接下來的程式碼將建立代理URL。
  7. proxyUrl, _ := url.Parse("http://" + proxyUser + ":" + proxyPass + "@" + proxyServer)
    • 這行程式碼使用url.Parse函式建立了一個代理URL。它將代理隧道的使用者名稱、密碼和代理伺服器地址組合成一個URL,格式為http://username:password@host:port_是忽略返回值的約定,因為返回值通常不需要使用。
  8. // 建立請求選項
    • 這一行是一個註釋,說明接下來的程式碼將建立一個grequests.RequestOptions結構體,用於配置HTTP請求。
  9. ro := &grequests.RequestOptions{
    • 這裡開始定義grequests.RequestOptions結構體變數ro
  10. Proxies: map[string]*url.URL{
    • 這裡定義了Proxies欄位,它是一個對映,將協議(如"http")對映到代理URL。
  11. "http": proxyUrl,
    • 這行程式碼將代理URL設定為HTTP協議的代理。
  12. },
    • 這是對映定義的結束。
  13. Headers: map[string]string{
    • 這裡定義了Headers欄位,它是一個對映,將HTTP頭欄位(如"user-agent")對映到相應的值。
  14. "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",
    • 這行程式碼設定了一個HTTP頭欄位,即使用者代理(User-Agent),用於標識發起請求的客戶端。
  15. },
    • 這是對映定義的結束。
  16. }
    • 這是grequests.RequestOptions結構體變數的定義結束。
  17. // 發起GET請求
    • 這一行是一個註釋,說明接下來的程式碼將發起一個GET請求。
  18. resp, err := grequests.Get("http://www.example.com", ro)
    • 這行程式碼使用grequests.Get函式發起一個GET請求。http://www.example.com是請求的目標URL,而ro是請求的配置選項。grequests.Get函式會返回一個Response物件和一個可能的錯誤。
  19. if err != nil {
    • 如果grequests.Get函式呼叫時發生錯誤,這個條件塊會執行。
  20. fmt.Println("Error:", err)
    • 這行程式碼列印出錯誤資訊。
  21. return
    • 這行程式碼表示如果發生錯誤,函式將返回,不繼續執行。
  22. }
    • 這是錯誤處理塊的結束。
  23. fmt.Println("Status code:", resp.StatusCode)
    • 如果請求成功,這行程式碼會列印出響應的狀態碼。
  24. fmt.Println("Response:", resp.String())

4.5 Gorequests 使用session

下面是使用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",
			},
		},
	}

五、HTTP服務端程式碼

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埠啟動伺服器
}

相關文章