gin框架中间件详解
gin框架涉及中间件相关有4个常用的方法,它们分别是c.Next()、c.Abort()、c.Set()、c.Get()。
中间件的注册
首先从常用的r := gin.Default()的Default函数开始看,它内部构造一个新的engine之后就通过Use()函数注册了Logger中间件和Recovery中间件:
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery()) // 默认注册的两个中间件
return engine
}
继续往下查看一下Use()函数的代码:
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...) // 实际上还是调用的RouterGroup的Use函数
engine.rebuild404Handlers()
engine.rebuild405Handlers()
return engine
}
从下方的代码可以看出,注册中间件其实就是将中间件函数追加到group.Handlers中:
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
而注册路由时会将对应路由的函数和之前的中间件函数结合到一起
也就是说,我们会将一个路由的中间件函数和处理函数结合到一起组成一条处理函数链条HandlersChain,而它本质上就是一个由HandlerFunc组成的切片
中间件的执行
c.Next()是非常关键的一步,它的代码很简单:
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
从上面的代码可以看到,这里通过索引遍历HandlersChain链条,从而实现依次调用该路由的每一个函数(中间件或处理请求的函数),这样便实现了在中间件函数中通过再次调用c.Next()实现嵌套调用(func1中调用func2;func2中调用func3),或者通过调用c.Abort()中断整个调用链条,从当前函数返回。
c.Set()/c.Get()
c.Set()和c.Get()这两个方法多用于在多个函数之间通过c传递数据的,比如我们可以在认证中间件中获取当前请求的相关信息(userID等)通过c.Set()存入c,然后在后续处理业务逻辑的函数中通过c.Get()来获取当前请求的用户。c就像是一根绳子,将该次请求相关的所有的函数都串起来了。
总结
- gin框架路由使用前缀树,路由注册的过程是构造前缀树的过程,路由匹配的过程就是查找前缀树的过程。
- gin框架的中间件函数和处理函数是以切片形式的调用链条存在的,我们可以顺序调用也可以借助
c.Next()方法实现嵌套调用。 - 借助
c.Set()和c.Get()方法我们能够在不同的中间件函数中传递数据。