需求是有些方法或操作需要在所有路由之前执行,这里就需要用到gin框架的中间件了。准确地说是构造自己的中间件,供gin框架调用、执行。还是先从main.go代码看起:
package main
import (
"study/route"
)
func main() {
router := route.RegisterRoutes();
// 绑定端口是8890
router.Run(":8890")
}
main.go文件内容很简单,就调用了route包里面的RegisterRoutes()方法,我们再看route.go文件:
package route
import (
"study/controller"
"github.com/gin-gonic/gin"
"study/util"
)
func RegisterRoutes() *gin.Engine {
router := gin.Default()
router.Use(util.Test())
router.GET("/fundinfoclient/:fundcode", controller.Func1)
router.GET("/", controller.Func2)
router.GET("/index", controller.Func2)
return router
}
跟上个帖子的route.go文件基本一样,就多了一句route.User(util.Test())。我们再接着看util.go文件:
package util
import (
"fmt"
"github.com/gin-gonic/gin"
)
func Test() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("1111111111")
c.Next()
fmt.Println("2222222222")
}
}
这个文件就是包含构造的中间件文件了,Test()方法就是我们构造的中间件。还有个controller文件这里就不展示了,是我们最终调用的路由。
我们先从route.go文件里的Use()方法看起,看这个方法是干了啥,看源码:
// Use attaches a global middleware to the router. ie. the middleware attached though 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
}
// 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()
}
func (group *RouterGroup) returnObj() IRoutes {
if group.root {
return group.engine
}
return group
}
上述三个方法是一个接着调用一个,最终返回group.engine。group.root之所以为true是在route.go文件中声明并赋值router时,在Default()方法中就已经令root值为true了。见源码:
func New() *Engine {
debugPrintWARNINGNew()
engine := &Engine{
RouterGroup: RouterGroup{
Handlers: nil,
basePath: "/",
root: true,
},
FuncMap: template.FuncMap{},
RedirectTrailingSlash: true,
RedirectFixedPath: false,
HandleMethodNotAllowed: false,
ForwardedByClientIP: true,
AppEngine: defaultAppEngine,
UseRawPath: false,
UnescapePathValues: true,
MaxMultipartMemory: defaultMultipartMemory,
trees: make(methodTrees, 0, 9),
delims: render.Delims{Left: "{{", Right: "}}"},
secureJsonPrefix: "while(1);",
}
engine.RouterGroup.engine = engine
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
return engine
}
// 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
}
实际是在New()方法中声明的,Default()方法是调用了New()方法,并在此基础上调用了Logger()和Recovery()两个中间件。如果不需要这两个中间件的话,之间调用New()方法赋值即可。
扯远了,再回到Use()方法,该方法的核心是调用的append()方法,也就是将中间件添加到handlers中。注意此时只是将中间件添加到handlers中,还没有调用!Use()方法最终返回的是添加了中间件的engine。然后在执行后面列出的所有路由,并逐一添加到handlers中。不管是GET、POST还是其他的请求,调用的都是下面这个方法:
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()
}
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
}
最终是调用的combineHandlers()方法将所有路由也添加到handlers中。接着我们再看调用了中间件以后的流程,也就是util.go中的Test()方法,返回的是一个匿名函数。函数体共三句,第一句和第三句简单,看看第二句c.Next()方法是干了啥:
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
这是Next()方法的实现,可以看到是遍历handlers,并调用,也就是说,之前添加的中间件以及路由是在c.Next()方法中调用执行的,并且是按添加进去的顺序依次执行!
当添加中间件语句放在所有路由前面时,我们看下结果:
可以看到,如果中间件放在所有路由的前面,那么所有路由的方法就如同c.Next()方法在Test()方法中的位置执行调用,被中间件渲染。
如果将中间件放在部分路由之后呢?
router.GET("/", controller.Func2)
router.GET("/index", controller.Func2)
譬如放在这两句之间,再来看看结果:
从结果可以看到,在引用中间件前面的路由,并没有被中间件渲染,而只有在它之后的路由才会被渲染。这点也可以从加入handlers的顺序可联想到。