gin框架之中间件

需求是有些方法或操作需要在所有路由之前执行,这里就需要用到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的顺序可联想到。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值