Gin 框架 -八-: 中間件
原創 劉慶輝 猿碼記
中間件(英語:
Middleware
),又譯中間件、中介層,是一類提供系統軟件和應用軟件之間連接、便於軟件各部件之間的溝通的軟件,應用軟件可以藉助中間件在不同的技術架構之間共享信息與資源。
1. 介紹
在Gin
框架中, 中間件本質上是gin.HandlerFunc 函數
,如果我們要自定義中間件,只需要返回類型是gin.HandlerFunc
即可。
在Gin
框架中, 使用中間件可分爲以下幾種場景:
-
全局使用
-
單個路由使用
-
路由組使用
2. 使用
2.1 全局使用
在gin.Default()
函數中,默認註冊全局中間件Logger、Recovery
,具體代碼如下:
func Default() *Engine {
// 打印debug信息
debugPrintWARNINGDefault()
// 創建引擎
engine := New()
// 註冊全局中間件
engine.Use(Logger(), Recovery())
return engine
}
2.2 單個路由使用
func main() {
engine := gin.New()
// 添加一箇中間件: Logger()
engine.GET("/route",gin.Logger(), func(context *gin.Context) {
context.JSON(200,gin.H{"msg":"針對單個路由"})
})
// 添加多箇中間件: Logger(),Recovery()
engine.GET("/route2",gin.Logger(),gin.Recovery(), func(context *gin.Context) {
context.JSON(200,gin.H{"msg":"針對單個路由添加多箇中間件"})
})
_ = engine.Run()
}
2.3 路由組使用
func main() {
engine := gin.New()
// 聲明路由後,通過Use註冊中間件
v1Group := engine.Group("/v1").Use(gin.Logger())
{
v1Group.GET("/middleGroup", func(context *gin.Context) {
context.JSON(200,gin.H{"msg":"succss"})
})
}
_ = engine.Run()
}
3. 自定義中間件
我們可以創建自己的中間件,在中間件中需要繼續執行時, 使用c.Next()
, 而要終止執行時則需調用c.Abort()
終止請求。
3.1 語法結構
func MiddleName() gin.HandlerFunc {
return func(context *gin.Context) {
// 請求前業務邏輯...
// 繼續往下執行
context.Next()
// context.Abort() 停止往下執行
// 請求後業務邏輯...
end := time.Now().Unix()
fmt.Printf("接口耗時:%v 秒 \n", end - t)
}
}
代碼說明:
-
MiddleName
: 自定義中間件名稱。 -
gin.HandlerFunc
: 中間件始終返回這個類型。 -
func(context *gin.Context)
: 匿名函數, 參數是上下文 (context *gin.Context
) -
context.Next()
: 繼續執行調用這個函數。 -
context.Abort()
:終止執行調用這個函數。
3.2 示例
// 聲明自定義中間件
func MyMiddleware() gin.HandlerFunc {
return func(context *gin.Context) {
// 請求前
fmt.Println("中間件-- 請求前")
t := time.Now().Unix()
// 繼續往下執行
context.Next()
// context.Abort() 停止往下執行
// 請求後
end := time.Now().Unix()
fmt.Printf("接口耗時:%v 秒 \n", end - t)
}
}
// 自定義中間件使用
func RunWithMyMiddle() {
engine := gin.New()
// 註冊自定義中間件
engine.GET("/route",MyMiddleware(), func(context *gin.Context) {
time.Sleep(time.Second * 3)
context.JSON(200,gin.H{"msg":"自定義路由"})
})
_ = engine.Run()
}
4. 多箇中間件
如果同時註冊多箇中間件,那麼他們之間的執行順序是什麼樣呢?
4.1 執行流程
4.2 代碼示例
// 自定義中間件A
func MyMiddleware() gin.HandlerFunc {
return func(context *gin.Context) {
// 請求前
fmt.Println("中間件1-- 請求前")
context.Next()
// 請求後
fmt.Println("中間件1-- 請求後")
}
}
// 自定義中間件B
func MyMiddleware2() gin.HandlerFunc {
return func(context *gin.Context) {
// 請求前
fmt.Println("中間件2-- 請求前")
context.Next()
// 請求後
fmt.Println("中間件2-- 請求後")
}
}
// 啓動服務
func main() {
engine := gin.New()
// 使用多箇中間件
engine.GET("/route",MyMiddleware(), MyMiddleware2(),func(context *gin.Context) {
time.Sleep(time.Second * 3)
context.JSON(200,gin.H{"msg":"自定義路由"})
})
_ = engine.Run()
}
請求控制檯輸出:
中間件1-- 請求前
中間件2-- 請求前
中間件2-- 請求後
中間件1-- 請求後
5. 實踐
創建自定義中間件用來判斷Token
是否有效 (是否是登錄狀態)。
5.1 源碼
./mian.go
代碼:
package main
import "go-use/practise"
func main() {
practise.RunServeWithCheckToken()
}
./practise/check_token.go
代碼:
package practise
import (
"github.com/gin-gonic/gin"
)
// 檢測token
func CheckTokenMiddle() gin.HandlerFunc {
return func(context *gin.Context) {
// 獲取token
token := context.DefaultQuery("token","")
// 檢測token
if token != "abcd" {
context.JSON(500,gin.H{"msg":"請先登錄!"})
// 終止請求
context.Abort()
}
// token合法則繼續往下執行
context.Next()
}
}
// 啓動服務
func RunServeWithCheckToken() {
engine := gin.Default()
// 登錄接口不需要添加檢測Token中間件
engine.GET("/user/login", func(context *gin.Context) {
context.JSON(200,gin.H{"msg":"登錄成功!","token":"abcd"})
})
// 需要驗證token的路由
user := engine.Group("/user").Use(CheckTokenMiddle())
{
// 用戶基本信息
user.GET("/info", func(context *gin.Context) {
data := map[string]interface{} {
"name": "張三",
"age": 18,
"likes": []string{"打遊戲","旅遊"},
}
context.JSON(200,gin.H{"msg":"請求成功","data":data})
})
// 更新用戶
user.GET("/update", func(context *gin.Context) {
context.JSON(200,gin.H{"msg":"請求成功"})
})
}
// 啓動服務
_ = engine.Run()
}
5.2 請求
# 不需要驗證token的接口
➜ curl -X GET http://127.0.0.1:8080/user/login
{"msg":"登錄成功!","token":"abcd"}
# 需要驗證Token,但是沒傳時
➜ curl -X GET http://127.0.0.1:8080/user/info
{"msg":"請先登錄!"}
# 需要驗證Token,並且已傳時
➜ curl -X GET http://127.0.0.1:8080/user/info?token=abcd
{"data":{"age":18,"likes":["打遊戲","旅遊"],"name":"張三"},"msg":"請求成功"}
6. 使用 Goroutine
當在中間件或 handler
中啓動新的 Goroutine
時,不能使用原始的上下文,必須使用只讀副本。
6.1 代碼示例
// 在中間件中使用Goroutine
func main() {
engine := gin.Default()
engine.Use(MyMiddleWithGoRoutine())
engine.GET("/useGo", func(context *gin.Context) {
context.JSON(200,gin.H{"msg":"success"})
})
_ = engine.Run()
}
// 在中間件中使用Goroutine
func MyMiddleWithGoRoutine() gin.HandlerFunc {
return func(context *gin.Context) {
// 複製上下文
cpContext := context.Copy()
go func() {
time.Sleep(3 * time.Second)
fmt.Println(cpContext.Request.URL.Path)
}()
context.Next()
}
}
7. 流行的中間件
在 gin-gonic/contrib(https://github.com/gin-gonic/contrib) 庫中整理很多常用的中間件,可以直接使用,具體中間件有以下:
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/1MP0rkgvX95nfeEQU5Bkjg