Gin
框架是一個用 Go 語言編寫的高效能 Web 框架,以其速度和簡潔性著稱。它由一個輕量級的 HTTP 路由器和一箇中介軟體架構組成,能夠處理大型流量並簡化開發者的工作。Gin
的主要特點包括內建的路由組、簡潔的 API 設計、強大的錯誤處理機制、支援多種格式的請求繫結和驗證,以及內建的日誌記錄功能。由於其效能優越和易於使用,Gin
廣泛應用於構建RESTful API
和 Web 服務。其設計理念是儘可能減少繁瑣的配置和程式碼,讓開發者專注於業務邏輯,實現快速開發和部署。
PS:據部分資料顯示,HTTP
路由的最佳化,速度提升了 10 倍以上。但我是查到的主流資料,效能提升沒這麼誇張。對比我知道的一些框架,QPS 提升還可以,大多數都是小於 3 倍的,但是記憶體消耗非常小,記憶體分配和分配時間非常小。主要原因也是因為 gin
採用了大量的 sync.Pool
最佳化,這一點跟 fasthttp
一樣的邏輯。所以池化技術是一個效能最佳化的大殺器。
下面我們來開始 gin
框架的實踐之旅。
依賴和安裝
- Go 語言環境
- 安裝
gin
:go get -u github.com/gin-gonic/gin - 開始擼程式碼
這是一個例子
首先我們先看一個最簡單的例子:
package main
import (
"github.com/gin-gonic/gin"
"net/http")
func main() {
//建立一個預設的路由引擎,預設使用了Logger和Recovery中介軟體
r := gin.Default()
//註冊一個路由和處理函式
r.GET("/", func(c *gin.Context) {
//返回一個字串,狀態碼是200
c.String(http.StatusOK, "Hello FunTester")
})
//監聽埠,預設是8080,這裡一般不設定IP,原因是在伺服器上可能有多個IP,如果設定了IP,就只能監聽這個IP
r.Run(":8000")
}
請求與響應
上面的每行程式碼的功能都已經寫好了註釋,是不是非常簡單,其中 GET
方法的引數型別是: handlers ...HandlerFunc
,而 HandlerFunc
的定義是 type HandlerFunc func(*Context)
,其中 gin.Context
是 Gin
框架中最核心的結構之一,它提供了上下文環境,供處理 HTTP 請求和響應。gin.Context
包含許多有用的方法和屬性,使開發者能夠輕鬆訪問請求資料、設定響應資料、處理錯誤以及在中介軟體和處理器之間傳遞資訊。
下面是 gin.Context
構造方法:
type Context struct {
writermem responseWriter
Request *http.Request
Writer ResponseWriter
Params Params
handlers HandlersChain
index int8
fullPath string
engine *Engine
params *Params
skippedNodes *[]skippedNode
// This mutex protects Keys map.
mu sync.RWMutex
// Keys is a key/value pair exclusively for the context of each request.
Keys map[string]any
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
Errors errorMsgs
// Accepted defines a list of manually accepted formats for content negotiation.
Accepted []string
// queryCache caches the query result from c.Request.URL.Query().
queryCache url.Values
// formCache caches c.Request.PostForm, which contains the parsed form data from POST, PATCH,
// or PUT body parameters.
formCache url.Values
// SameSite allows a server to define a cookie attribute making it impossible for
// the browser to send this cookie along with cross-site requests.
sameSite http.SameSite
}
相信看到這裡,你一定若有所悟了吧。gin
框架用來處理請求響應都是靠 gin.Context
而請求和響應都包含在 gin.Context
當中。
gin
框架的請求方法是透過方法名直接設定的,例如上面例子中的 r.GET
就表示這個介面註冊了 GET
方法的呼叫。下面是 gin
支援的呼叫方法:
- GET:用於從伺服器獲取資源。
- POST:用於向伺服器提交資料,通常用於建立資源。
- PUT:用於更新伺服器上的資源。
- DELETE:用於刪除伺服器上的資源。
- PATCH:用於部分更新伺服器上的資源。
- HEAD:類似於 GET 請求,但只返回響應頭,用於獲取資源的後設資料。
- OPTIONS:用於獲取伺服器支援的 HTTP 方法。
- TRACE:用於回顯伺服器收到的請求,主要用於診斷。
這些方法覆蓋了基本的 HTTP 操作,允許客戶端與伺服器進行各種型別的互動。
下面我們看看處理響應的幾種方法:
JSON 響應
router.GET("/json", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "hello",
"status": "success",
})
})
String 響應
router.GET("/string", func(c *gin.Context) {
c.String(http.StatusOK, "Hello, world")
})
HTML 響應
router.LoadHTMLGlob("templates/*")
router.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "Main website",
})
})
當然還有其他格式的響應,這裡就不演示了。
引數解析
路徑引數
在 URL
路徑當中引數是最常見的一種型別,在 gin
框架當中,針對這種情況設定了兩種型別。第一種是普通的路徑引數,另一種是正則匹配的 URL 地址。一開始我也有點懵,下面來看程式碼演示。
router.GET("/user/:id/*path", func(c *gin.Context) {
id := c.Param("id")
path := c.Param("path")
// 根據id獲取使用者資訊或處理其他邏輯
c.String(http.StatusOK, "User ID: %s Path: %s", id, path)
})
這裡 id
就是普通的路徑引數, path
就是匹配引數。假如請求路徑是 /user/1/age
匹配結果就是 id=1
和 path=/age
。假如路徑是 /user/2/account/balance
,那麼 id=2
,而 path=/account/balance
這下就清楚這兩者區別,通常不咋會用到匹配的引數。
query 引數
獲取 Get 請求引數的常用函式:
- func (c Context) Query*(key string) string
- func (c Context) DefaultQuery*(key, defaultValue string) string
- func (c Context) GetQuery*(key string) (string, bool)
演示程式碼:
func Handler(c *gin.Context) {
//獲取name引數, 透過Query獲取的引數值是String型別。
name := c.Query("name")
//獲取name引數, 跟Query函式的區別是,可以透過第二個引數設定預設值。
name := c.DefaultQuery("name", "tizi365")
//獲取id引數, 透過GetQuery獲取的引數值也是String型別,
// 區別是GetQuery返回兩個引數,第一個是引數值,第二個引數是引數是否存在的bool值,可以用來判斷引數是否存在。
id, ok := c.GetQuery("id")
if !ok {
// 引數不存在
}
}
POST 引數
獲取 Post 請求引數的常用函式:
- func (c Context) PostForm*(key string) string
- func (c Context) DefaultPostForm*(key, defaultValue string) string
- func (c Context) GetPostForm*(key string) (string, bool)
演示程式碼如下:
func Handler(c *gin.Context) {
//獲取name引數, 透過PostForm獲取的引數值是String型別。
name := c.PostForm("name")
// 跟PostForm的區別是可以透過第二個引數設定引數預設值
name := c.DefaultPostForm("name", "tizi365")
//獲取id引數, 透過GetPostForm獲取的引數值也是String型別,
// 區別是GetPostForm返回兩個引數,第一個是引數值,第二個引數是引數是否存在的bool值,可以用來判斷引數是否存在。
id, ok := c.GetPostForm("id")
if !ok {
// 引數不存在
}
}
物件引數
前面獲取引數的方式都是一個個引數的讀取,比較麻煩,Gin
框架支援將請求引數自動繫結到一個struct
物件,這種方式支援 Get/Post 請求,也支援HTTP
請求body
內容為json/xml
格式的引數。
type User struct {
Name string `json:"name" form:"name"`
Age int `json:"age" form:"age"`
}
演示程式碼如下:
user.GET("/login", func(c *gin.Context) {
u := &User{}
c.ShouldBind(u)
c.JSON(http.StatusOK, u)
})
分組路由
在 Gin 框架中,分組路由(Route Groups)是用於組織路由和中介軟體的一種有效方式。分組路由可以幫助你將相關的路由和中介軟體組織在一起,使程式碼更加清晰和易於維護。以下是如何在 Gin 框架中使用分組路由的示例:
user := r.Group("/user")
{
user.GET("/login", func(c *gin.Context) {
u := &User{}
c.ShouldBind(u)
c.JSON(http.StatusOK, u)
})
}
非常簡單的語法,只需要註冊一個 Group
即可,然後組內註冊的 HandlerFunc
的路由字首會加上組的 relativePath
也就是 /user
。
中介軟體
在 Gin 框架中,中介軟體(Middleware)是一個函式,它可以在處理請求之前或之後執行特定的操作。中介軟體通常用於執行一些通用的任務,比如日誌記錄、身份驗證、跨域資源共享(CORS)處理等。Gin 框架支援全域性中介軟體、路由組中介軟體和單個路由中介軟體。
基於 gin.Context
強大的功能,以及 gin
框架的優秀設計,中介軟體的實現方法依舊是返回 HandlerFunc
即可。下面展示一個列印日誌的中介軟體。
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
// 開始時間
t := time.Now()
// 處理請求
c.Next()
// 計算執行時間
latency := time.Since(t)
log.Printf("Latency: %v", latency)
// 獲取響應狀態碼
status := c.Writer.Status()
log.Printf("Status: %d", status)
}
}
中介軟體的應用的話,有 3 種型別:全域性、一組路由、單個路由。下面一次展示使用方法:
全域性:
func main() {
router := gin.Default()
// 應用全域性中介軟體
router.Use(Logger())
router.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "pong")
})
router.Run(":8080")
}
針對一組路由:
func main() {
router := gin.Default()
// 定義一個路由組
apiGroup := router.Group("/api")
{
// 應用中介軟體到路由組
apiGroup.Use(Logger())
apiGroup.GET("/users", func(c *gin.Context) {
c.String(http.StatusOK, "List of users")
})
apiGroup.GET("/user/:id", func(c *gin.Context) {
id := c.Param("id")
c.String(http.StatusOK, "User ID: %s", id)
})
}
router.Run(":8080")
}
針對單個路由:
func main() {
router := gin.Default()
// 應用中介軟體到單個路由
router.GET("/admin", Logger(), func(c *gin.Context) {
c.String(http.StatusOK, "Admin page")
})
router.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "pong")
})
router.Run(":8080")
}
下面是我列印響應結果的中介軟體實踐:
func Check() gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("example", "12345")
//path := c.Request.URL.Path
w := &responseWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = w
c.Next()
responseBody := w.body.String()
logger.Info("response", zap.String("response", responseBody))
}
}
type responseWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w responseWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
其他
還有一些其他的常用 API,下面我撿著自己學到的展示一下。
獲取客戶端 IP:c.ClientIP()
設定模式:
// 設定 release模式
//gin.SetMode(gin.ReleaseMode)
// 或者 設定debug模式
gin.SetMode(gin.DebugMode)
Gin 框架提供了兩種執行模式:Release 模式和Debug 模式。這兩種模式主要區別在於日誌輸出和錯誤處理方面。透過設定不同的模式,開發者可以更好地適應開發和生產環境的需求。這個看需求選用吧。
- 2021 年原創合集
- 2022 年原創合集
- 2023 年原創合集
- 服務端功能測試
- 效能測試專題
- Java、Groovy、Go、Python
- 單元&白盒&工具合集
- 測試方案&BUG&爬蟲&UI 自動化
- 測試理論雞湯
- 社群風采&影片合集