Gin中间件相遇(二)

17 阅读2分钟

本篇文章补充了中间件的使用,以及简单(简陋)的原理解析;

中间件的使用补充

中间件可以只在匹配一个路径时使用中间件,router.GET函数中,第二个参数是可变参数,可以传入多个中间件

router.GET("/blog_detail.html", getBlogDetail, gin.Logger())

也可以分组匹配:

roup("/v1")
router.Use(gin.Logger())
{
    router.GET("/blog", getBlogs)
}

自定义中间件

比如定义一个在方法执行前和执行后都打印提示的中间件

func MyLogger() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("请求开始处理。。。")
		c.Next()
		fmt.Println("请求处理结束。。。")
	}
}

只在访问一个路径时使用:

router.GET("/blog_list.html", getBlogList, MyLogger())

效果: xiaoguo.png

gin中中间件有四个常用的方法:c.Next()、c.Abort()、c.Set()、c.Get();这里要注意的是c.Next方法,这个是执行后续中间件请求处理的意思,如果中间件拦截了这个请求后要在请求处理前后都有操作,就需要把c.Next()放到中间,调用c.Next会执行下一个中间件;像嵌套或者树的前序遍历一样,先往下执行,到底再往上返回;

原理浅析

中间件注册

首先看默认中间件的初始化实现流程:gin.Default():

func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery()) // 默认注册两个中间件
	return engine
}

继续查看engine.Use()函数的代码,其中调用engine.RouterGroup.Use,HandlerFunc就是指中间件,这里可以传入多个中间件;

func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	engine.RouterGroup.Use(middleware...) //调用的RouterGroup的Use函数
	engine.rebuild404Handlers()
	engine.rebuild405Handlers()
	return engine
}

调用append()就是将传入的中间件追加到group.Handlers,而group.Handlers就是HandlersChain类型,也就是一个切片:type HandlersChain []HandlerFunc

func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
	group.Handlers = append(group.Handlers, middleware...)
	return group.returnObj()
}

实际注册路由时,会将对应路由的函数和中间件函数合并在一起,这个合并的过程也很有意思,创建一个长度等于中间件和路由函数的切片;用复制函数,把中间件函数复制到前面的部分,路由函数复制到后面的部分;

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

以上就是本篇文章的内容了,期待下次相遇。