Go Web輕量級框架Gin學習系列:中介軟體使用詳解

張君鴻發表於2019-04-29

在今天這篇文章中,我們來談談Gin框架中介軟體(middleware)的使用,應該說Gin的中介軟體是Gin框架中一個非常重要的內容,而且Gin中介軟體也是使用Gin框架開發一個完整Web程式時不可或缺的部分,所以有必要好了解一下。

什麼是Gin中介軟體

Gin中介軟體是什麼?Gin中介軟體的作用是什麼?要怎麼樣使用中介軟體呢?

1. Gin中介軟體的作用

好吧,簡單來說,Gin中介軟體的作用有兩個:

  1. Web請求到到達我們定義的HTTP請求處理方法之前,攔截請求並進行相應處理(比如:許可權驗證,資料過濾等),這個可以類比為前置攔截器前置過濾器

  2. 在我們處理完成請求並響應客戶端時,攔截響應並進行相應的處理(比如:新增統一響應部頭或資料格式等),這可以型別為後置攔截器後置過濾器

2. Gin中介軟體的定義

在Gin框架中,中介軟體的型別定義如下程式碼所示,可以看出,中介軟體實際上就是一個以gin.Context為形參的函式而已,與我們定義處理HTTP請求的Handler本質上是一樣的,並沒有什麼神祕可言。

type HandlerFunc func(*Context)
複製程式碼

3. Gin內建中介軟體

在使用Gin框架開發Web應用時,常常需要自定義中介軟體,不過,Gin也內建一些中介軟體,我們可以直接使用,下面是內建中介軟體列表:

func BasicAuth(accounts Accounts) HandlerFunc
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc
func Bind(val interface{}) HandlerFunc //攔截請求引數並進行繫結
func ErrorLogger() HandlerFunc       //錯誤日誌處理
func ErrorLoggerT(typ ErrorType) HandlerFunc //自定義型別的錯誤日誌處理
func Logger() HandlerFunc //日誌記錄
func LoggerWithConfig(conf LoggerConfig) HandlerFunc
func LoggerWithFormatter(f LogFormatter) HandlerFunc
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc
func Recovery() HandlerFunc
func RecoveryWithWriter(out io.Writer) HandlerFunc
func WrapF(f http.HandlerFunc) HandlerFunc //將http.HandlerFunc包裝成中介軟體
func WrapH(h http.Handler) HandlerFunc //將http.Handler包裝成中介軟體
複製程式碼

中間的使用

1. 不使用預設中介軟體

使用gin.Default()返回的gin.Engine時,已經預設使用了RecoveryLogger中介軟體,從下面gin.Default()方法的原始碼可以看出:

func Default() *Engine {
    debugPrintWARNINGDefault()
    engine := New()
    engine.Use(Logger(), Recovery())//使用Recovery和Logger中間
    return engine
}
複製程式碼

當我們不想使用這兩個中介軟體時,可以使用gin.New()方法返回一個不帶中介軟體的gin.Engine物件:

router := gin.New()//不帶中介軟體
複製程式碼

2. 全域性使用中介軟體

直拉使用gin.Engine結構體的Use()方法便可以在所有請求應用中介軟體,這樣做,中介軟體便會在全域性起作用。

router := gin.New()
router.Use(gin.Recovery())//在全域性使用內建中介軟體
複製程式碼

3. 路由分組使用中介軟體

更多的時候,我們會根據業務不同劃分不同路由分組(RouterGroup ),不同的路由分組再應用不同的中介軟體,這樣就達到了不同的請求由不同的中介軟體進行攔截處理。

router := gin.New()
user := router.Group("user", gin.Logger(),gin.Recovery())
{
    user.GET("info", func(context *gin.Context) {

    })
    user.GET("article", func(context *gin.Context) {

    })
}
複製程式碼

4. 單個路由使用中介軟體

除了路由分組,單個請求路由,也可以應用中介軟體,如下:

router := gin.New()
router.GET("/test",gin.Recovery(),func(c *gin.Context){
    c.JSON(200,"test")
})
複製程式碼

也可以在單個路由中使用多箇中介軟體,如下:

router := gin.New()
router.GET("/test",gin.Recovery(),gin.Logger(),func(c *gin.Context){
    c.JSON(200,"test")
})
複製程式碼

自定義中介軟體

上面的講解中,我們看到,雖然Gin提供了一些中介軟體,我們直接使用即可,但內建中介軟體可能滿足不我們業務開發的需求,在開發過程中我們需要開自己的中介軟體,這在Gin框架中是非常簡單的一件事。

在前面,我們看到Gin框架自帶的中介軟體方法,都是返回HandlerFunc型別,其定義如下:

type HandlerFunc func(*Context)
複製程式碼

HandlerFunc規範了Gin中介軟體的定義,所以自定義中介軟體,如下:

//定義中介軟體
func MyMiddleware(c *gin.Context){
    //中介軟體邏輯    
}

複製程式碼

定義好中介軟體,便可使用中介軟體,這裡演示的是全域性使用,也可以在單個路由或路由分組中使用:

router = gin.Default()
router.Use(MyMiddleware)
複製程式碼

或者,通過自定義方法,返回一箇中介軟體函式,這是Gin框架中更常用的方式:

//定義一個返回中介軟體的方法
func MyMiddleware(){
    //自定義邏輯
    
    //返回中介軟體
    return func(c *gin.Context){
        //中介軟體邏輯
    }
}
複製程式碼

使用自定義的中介軟體,注意MyMiddleware方法後面有加括號:

router = gin.Default()
router.Use(MyMiddleware())
複製程式碼

資料傳遞

當我們在中介軟體攔截並預先處理好資料之後,要如何將資料傳遞我們定義的處理請求的HTTP方法呢?可以使用gin.Context中的Set()方法,其定義如下,Set()通過一個key來儲存作何型別的資料,方便下一層處理方法獲取。

func (c *Context) Set(key string, value interface{})
複製程式碼

當我們在中介軟體中通過Set方法設定一些數值,在下一層中介軟體或HTTP請求處理方法中,可以使用下面列出的方法通過key獲取對應資料。

其中,gin.Context的Get方法返回interface{},通過返回exists可以判斷key是否存在。

func (c *Context) Get(key string) (value interface{}, exists bool)
複製程式碼

當我們確定通過Set方法設定對應資料型別的值時,可以使用下面方法獲取應資料型別的值。

func (c *Context) GetBool(key string) (b bool)
func (c *Context) GetDuration(key string) (d time.Duration)
func (c *Context) GetFloat64(key string) (f64 float64)
func (c *Context) GetInt(key string) (i int)
func (c *Context) GetInt64(key string) (i64 int64)
func (c *Context) GetString(key string) (s string)
func (c *Context) GetStringMap(key string) (sm map[string]interface{})
func (c *Context) GetStringMapString(key string) (sms map[string]string)
func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string)
func (c *Context) GetStringSlice(key string) (ss []string)
func (c *Context) GetTime(key string) (t time.Time)
複製程式碼

示例程式碼:

//自定義中介軟體
func MyMiddleware(c *gin.Context){
    c.Set("mykey",10)
}

router := gin.New()
router.GET("test",MyMiddleware,func(c *gin.Context){
    c.GetInt("mykey")//我們知道設定進行的是整型,所以使用GetInt方法來獲取
})
複製程式碼

攔截請求與後置攔截

1. 攔截請求

我們說過,中介軟體的最大作用就是攔截過濾請求,比如我們有些請求需要使用者登入或者需要特定許可權才能訪問,這時候便可以中介軟體中做過濾攔截,當使用者請求不合法時,可以使用下面列出的gin.Context的幾個方法中斷使用者請求:

下面三個方法中斷請求後,直接返回200,但響應的body中不會有資料。

func (c *Context) Abort()
func (c *Context) AbortWithError(code int, err error) *Error
func (c *Context) AbortWithStatus(code int)
複製程式碼

使用AbortWithStatusJSON()方法,中斷使用者請求後,則可以返回json格式的資料.

func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{})
複製程式碼

2. 後置攔截

前面我們講的都是到達我們定義的HTTP處理方法前進行攔截,其實,如果在中介軟體中呼叫gin.ContextNext()方法,則可以請求到達並完成業務處理後,再經過中介軟體後置攔截處理,Next()方法定義如下:。

func (c *Context) Next()
複製程式碼

在中介軟體呼叫Next()方法,Next()方法之前的程式碼會在到達請求方法前執行,Next()方法之後的程式碼則在請求方法處理後執行:

func MyMiddleware(c *gin.Context){
    //請求前
    c.Next()
    //請求後
}
複製程式碼

示例程式碼

func MyMiddleware(c *gin.Context){
    c.Set("key",1000)//請求前
    c.Next()
    c.JSON(http.StatusOK,c.GetInt("key"))//請求後
}

router := gin.New()
router.GET("test", MyMiddleware, func(c *gin.Context) {
    k := c.GetInt("key")
    c.Set("key", k+2000)
})
router.Run()
複製程式碼

上面示例程式執行結果為3000,通過上面這樣一個簡單的示例程式,我們可以看到中介軟體在請求攔截請求,處理資料並控制Web請求流程的作用。

小結

學習Gin框架,中介軟體middleware非常重要的一塊知識,它可以我們定義處理HTTP請求的方法前攔截不合法的HTTP請求,或者預先處理好資料,或響應時新增統一的響應頭部,因此在使用Gin開發Web應用時,中介軟體是必用的知識。

相關文章