Go-Gin中間件初探

1 前言

中間件,顧名思義,它是一個處於中間的組件,誰和誰的中間呢?一般是請求入口和執行方法中間。

在Gin的整個實現中,中間件可謂是Gin的精髓。一個個中間件組成一條中間件鏈,對HTTP Request請求進行攔截處理,實現了代碼的解耦和分離,並且中間件之間相互不用感知到,每個中間件只需要處理自己需要處理的事情即可。今天我們就通過這篇文章,詳細的介紹Gin中間的使用和原理。

2 Gin的默認中間件

在Gin中,我們可以通過Gin提供的默認函數,來構建一個自帶默認中間件的*Engine

r := gin.Default()
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

可以看到 Default() 返回了一個帶着Logger和Recovery中間件的引擎實例。

// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
// Create an instance of Engine, by using New() or Default()
type Engine struct {
	RouterGroup
    .
    .
    .
}

引擎是框架的實例,包含了muxer,中間件和配置。

Engine.RouteGroup 是一個路由分組,用來內部配置路由,與 一個路由前綴 和 一組 handlers(中間件 )聯繫起來。

// RouterGroup is used internally to configure router, a RouterGroup is associated with
// a prefix and an array of handlers (middleware).
type RouterGroup struct {
	Handlers HandlersChain
	basePath string
	engine   *Engine
	root     bool
}

Engine.RouteGroup.Handles 是一個 HandlerFunc 列表;

// HandlersChain defines a HandlerFunc slice.
type HandlersChain []HandlerFunc

HandlerFunc 是一個函數,它定義了handler,這個handler被gin中間件當做返回值來使用。 

// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)

 

從上面的代碼可以看出,使用 gin.Default() 會返回一個框架實例,這個實例帶有 路由分組,而路由分組含有一組中間件gin.HandlerFunc,每個中間件實際上就是一個以 *Context 爲參數的函數。

 

engine.Use(Logger(), Recovery())
// Use attaches a global middleware to the router. i.e. the middleware attached through Use() will be
// included in the handlers chain for every single request. Even 404, 405, static files...
// For example, this is the right place for a logger or error management middleware.
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	engine.RouterGroup.Use(middleware...)
	engine.rebuild404Handlers()
	engine.rebuild405Handlers()
	return engine
}

而 engine 是通過 .Use(middleware ...HandlerFunc) 來把 不定多個 HandlerFunc 串起來用的,也就意味着,我們可以從前到後,先後使用多箇中間件。

3 自定義中間件

我們知道一箇中間件就是 gin.HandlerFunc,就是一個以 *Context 爲參數的函數。那我們嘗試着自己定義一箇中間件。

func costTime() gin.HandlerFunc {
	return func(c *gin.Context) {
		//請求前獲取當前時間
		nowTime := time.Now()

		//請求處理
		c.Next()

		//處理後獲取消耗時間
		costTime := time.Since(nowTime)
		url := c.Request.URL.String()
		fmt.Printf("the request URL %s cost %v\n", url, costTime)
	}
}

我們看到,costTime() 返回的是一個 以 *gin.Context 爲參數的函數,也就是 gin.HandlerFunc 結構。它在執行了 c.Next() 方法後,打印出消耗時間。

這裏的 c.Next() 方法,是去執行後續中間件請求處理,含當前中間件之後的、還未執行的中間件,以及我們定義的處理方法。

我們來試驗一下:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"time"
)

func main() {
	r := gin.Default()

	r.Use(costTime())

	r.GET("/", func(c *gin.Context) {
		c.JSON(200, "首頁")
	})

	r.Run(":8080")
}

func costTime() gin.HandlerFunc {
	return func(c *gin.Context) {
		//請求前獲取當前時間
		nowTime := time.Now()

		//請求處理
		c.Next()

		//處理後獲取消耗時間
		costTime := time.Since(nowTime)
		url := c.Request.URL.String()
		fmt.Printf("the request URL %s cost %v\n", url, costTime)
	}
}

記錄

[GIN-debug] GET    /                         --> main.main.func1 (4 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8080
the request URL / cost 603.209µs
[GIN] 2023/03/21 - 22:14:54 | 200 |     715.917µs |             ::1 | GET      "/"

這裏可以看到 the request URL / cost 603.209µs 這個日誌。

在日誌的第一行,我們注意到 4 handlers 這些信息,也就是說,這個請求經歷過了4個 handlers,我們整理一下:

  • gin.Default() 裏面框架引擎默認Use 的 Logger() 和 Recovery() 中間件;
  • 被Use 的 自定義 costTime() 中間件;
  • 用來處理的 GET / 請求的 
    func(c *gin.Context) {
        c.JSON(200, "首頁")
    }

這更加證明了,handler 就是一個以 *Context 爲參數的函數 這個概念。

 

至此,我們初步對Gin的中間件有了一點了解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章