日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Gin源码解析和例子——中间件(middleware)

發布時間:2023/11/27 生活经验 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Gin源码解析和例子——中间件(middleware) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

? ? ? ? 在《Gin源碼解析和例子——路由》一文中,我們已經初識中間件。本文將繼續探討這個技術。(轉載請指明出于breaksoftware的csdn博客)

? ? ? ? Gin的中間件,本質是一個匿名回調函數。這和綁定到一個路徑下的處理函數本質是一樣的。

? ? ? ? 再以Engine的Default方法為例

func Default() *Engine {debugPrintWARNINGDefault()engine := New()engine.Use(Logger(), Recovery())return engine
}

? ? ? ? 第4行就讓該Engine使用了Logger和Revoery兩個中間件。Use方法將新增的中間件加入到中間件集合中

// Use adds middleware to the group, see example code in github.
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {group.Handlers = append(group.Handlers, middleware...)return group.returnObj()
}

? ? ? ? 因為是append,所以后加入的中間件排在集合后面。理解這個特性對我們正確使用中間件很重要。

? ? ? ? 再回顧下之前介紹的路由的代碼

	r := gin.Default()// Ping testr.GET("/ping", func(c *gin.Context) {c.String(http.StatusOK, "pong")})

? ? ? ? host:port/ping下的請求,將被路由到輸出pong的匿名函數里。GET方法封裝了handle方法

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {absolutePath := group.calculateAbsolutePath(relativePath)handlers = group.combineHandlers(handlers)group.engine.addRoute(httpMethod, absolutePath, handlers)return group.returnObj()
}

? ? ? ? 這兒注意下第3行,上面這個匿名函數似乎是和其他匿名函數合并成一個匿名函數集合。然后再在第4行和絕對路徑綁定。

func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {finalSize := len(group.Handlers) + len(handlers)if finalSize >= int(abortIndex) {panic("too many handlers")}mergedHandlers := make(HandlersChain, finalSize)copy(mergedHandlers, group.Handlers)copy(mergedHandlers[len(group.Handlers):], handlers)return mergedHandlers
}

? ? ? ? 這兒合并的就是中間件集合(group.Handlers)。第7~8行代碼,告訴我們中間件的回調要先于用戶定義的路徑處理函數。那么上例中,mergeHandlers中的成員是【logger回調,recovery回調,GET的匿名回調】。

? ? ? ? 這樣,每個路徑的回調函數鏈都將包含中間件的回調,即【logger回調,recovery回調】。

? ? ? ? 我再看一個最簡單的中間件的實現

func MiddlewareDemo() gin.HandlerFunc {return func(c *gin.Context) {c.Next()}
}

? ? ? ? 這個中間件只是返回了一個匿名函數,該函數內部需要調用Conext的Next函數來驅動執行之后的handler。

func (c *Context) Next() {c.index++for s := int8(len(c.handlers)); c.index < s; c.index++ {c.handlers[c.index](c)}
}

? ? ? ? 這也是Gin設計中比較奇葩的地方:

  • Context的Next方法讓合并之后的handlers中的回調執行
  • handlers中的回調調用Context的Next方法以驅動下個回調執行

? ? ? ? 如果我們不看Next的實現,單從上面的話中可以感覺到似乎邏輯進入了一種異常循環的狀態。其實Gin使用了一個Context中的index變量來解決了這個問題。于是中間件、框架和路徑對應的回調之前的關系是

? ? ? ? 我們看個例子

package mainimport ("log""net/http""github.com/gin-gonic/gin"
)func MiddlewareA() gin.HandlerFunc {return func(c *gin.Context) {log.Println("MiddlewareA before request")// before requestc.Next()// after requestlog.Println("MiddlewareA after request")}
}func MiddlewareB() gin.HandlerFunc {return func(c *gin.Context) {log.Println("MiddlewareB before request")// before requestc.Next()// after requestlog.Println("MiddlewareB after request")}
}// This function's name is a must. App Engine uses it to drive the requests properly.
func main() {// Starts a new Gin instance with no middle-warer := gin.New()r.Use(MiddlewareA(), MiddlewareB())r.GET("/ping", func(c *gin.Context) {c.String(http.StatusOK, "pong")log.Println("pong")})r.Run(":8080")
}

? ? ? ? 觸發一次請求后,服務器的日志輸出是

2018/12/03 16:07:30 MiddlewareA before request
2018/12/03 16:07:30 MiddlewareB before request
2018/12/03 16:07:30 pong
2018/12/03 16:07:30 MiddlewareB after request
2018/12/03 16:07:30 MiddlewareA after request

? ? ? ? 可以看到,結果符合我們對代碼的解讀。

總結

以上是生活随笔為你收集整理的Gin源码解析和例子——中间件(middleware)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。