Gin中間件(重要)
Gin框架允許開發者在處理請求的過程中,加入用戶自己的鉤子(Hook)函數。
這個鉤子函數就叫中間件,中間件適合處理一些公共的業務邏輯,比如登錄校驗、日志列印、耗時統計等。
例子:中間件
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" ) func indexHandler(c *gin.Context){ fmt.Println("index") name, ok := c.GET("name") if !ok { name = "匿名用戶" } c.JSON(http.StatusOK, gin.H{ "msg": name, }) } func m1(c *gin.Context){ fmt.Println("m1 in...") start := time.Now() c.Next() // 調用後續的處理函數 cost := time.Since(start) fmt.Printf("cost:%v\n", cost) fmt.Println("m1 out...") } func m2(c *gin.Context){ fmt.Println("m2 in...") c.set("name", "qimi") // m1裡做一些操作,m2裡拿到 // return // 到這裡直接退出,不會列印出m2 out... fmt.Println("m2 out...") } func authMiddleware(doCheck bool)gin.HandlerFunc{ // 或這些其他準備工作 return func(c *gin.Context){ if doCheck { // 是否登錄的判斷 // c.Next() // c.Abort() } else { c.Next() } } } func main(){ r := gin.Default() // 默認使用Logger()和Recovrey() r.Use(m1, m2, authMiddleware(true)) // 全局注冊中間件函數 m1 m2 r.GET("/index", indexHandler) r.GET("/shop", func(c *gin.Context){ c.JSON(http.StatusOK, gin.H{ "msg": "index", }) }) r.GET("/user", func(c *gin.Context){ c.JSON(http.StatusOK, gin.H{ "msg":"index ", }) }) /* (全局已經注冊) xxGroup := r.Group("/xx", authMiddleware(true)) { xx.Group.GET("/index", func(c *gin.Context){ c.JSON(http.StatusOK, gin.H{"msg": "xxGroup"}) }) } xx2Group := r.Group("/xx2") xx2Group.Use(authMiddleware(true)) { xx2Group.Get("/index", func(c *gin.Context){ c.JSON(http.StatusOK, fin.H{"msg": "xx2Group"}) }) } */ r.Run() // :8080 }
定義中間件
Gin的中間件必須是一個gin.HandlerFunc類型。例如下面代碼一樣:定義一個中間件。
// StatCost 是一個統計耗時請求耗時的中間件 func StatCost() gin.HandlerFunc { return func (c *gin.Context) { start := time.Now() c.Set("name", "小王子") c.Next() // c.Abort() cost := time.Since(start) log.Println(cost) } }
注冊中間件
在gin框架中,可以爲每個路由添加任意數量的中間件。
爲全局路由注冊
func main(){ // 注冊一個全局中間件 r.Use(StatCost()) r.Get("/test", func(c *gin.Context){ name := c.MustGet("name").(string) // 從上下文取值 log.Println(name) c.JSON(http.StatusOK, gin.H{ "message": "Hello world!" }) }) r.Run() }
爲某個路由單獨注冊
// 給/test2路由單獨注冊中間件(可注冊多個) r.GET("/test2", StatCost(), func(c *gin.Context){ name := c.MustGet("name").(string) // 從上下文取值 log.Println(name) c.JSON(http.StatusOK, gin.H{ "message": "Hello world!", }) })
爲路由組注冊中間件
寫法1:
shopGroup := r.Group("/shop", StatCost()) { shopGroup.GET("/index", func(c *gin.Context){...}) ... }
寫法2:
shopGroup := r.Group("/shop") shopGroup.Use(StatCost()) { shopGroup.GET("/index", func(c *gin.Context){...}) ... }
中間件注意事項
gin默認中間件件
Gin.Default()默認使用了Logger 和 Recovery 中間件,其中:
Logger中間件將日志寫入gin.DefaultWriter,即使配置了GIN_MODE = release
Recovery 中間件會revover任何panic。如果有panic的話,會寫入500響應碼。
如果不想使用上面兩個默認的中間件,可以使用gin.New()新建一個沒有任何默認中間件的路由
gin中間件中使用goroutine
當在中間件或handler中啓動新的goroutine時,不能使用原始的上下文(c *gin.Context),必須使用其只讀副本(c.Copy())。
例如:
func m1(c *gin.Context){ go funcXX(c.Copy()) }