gin框架的中间件机制介绍
参考
https://zhuanlan.zhihu.com/p/157430262
https://juejin.cn/post/7034338727883177997
Gin默认中间件
在Gin中,我们可以通过Gin提供的默认函数,来构建一个自带默认中间件的
*Engine
。
r := gin.Default()
Default
内部自动挂一个Logger
中间件用于日志,一个Recovery
用于程序panic
异常的统一处理恢复,不至于让程序直接报错停止!
// 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
}
从中我们可以看到,Gin的中间件是通过Use
方法设置的,它接收一个可变参数,所以我们同时可以设置多个中间件。
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...) // 对传入的函数,append到Handlers切片中!
return group.returnObj()
}
一个Gin的中间件,其实就是Gin定义的一个HandlerFunc
,而它在我们Gin中经常使用,比如:
r.GET("/", func(c *gin.Context) {
fmt.Println("首页")
c.JSON(200, "")
})
中间件被调用!
那么中间件被调用的时候,那么其实也是和路由匹配是一样的,都要从路由匹配开始!获取path对应的树,然后在调用对应的handleFunction
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
通过Next函数一直去循环调用所有的handlers
直到把所有的 handers
执行完成
关于 gin.Context
相关常用方法
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),
Abort
Abort
经常用来直接退出中间件的操作,比如我们的用户授权校验中间件,如果校验不通过那么就直接在当前中间件退出了!
其实内部实现也非常简单!
func (c *Context) Abort() {
c.index = abortIndex
}
直接让当前 gin contenxt 中 循环执行handler的索引 index 直接等于最大限制的索引,
满足不了 Next
中的这个条件了 c.index < int8(len(c.handlers))
满足不了的原因,因为我们 combineHandlers 已经限制了我们中间件的长度不可能超过 abortIndex
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
// 计算全部handler的一个长度!
finalSize := len(group.Handlers) + len(handlers)
//不能超出一个 abortIndex的长度
assert1(finalSize < int(abortIndex), "too many handlers")
// 根据长度去初始化一个新的指定长度的切片
mergedHandlers := make(HandlersChain, finalSize)
// 然后把所有的handler整合到一个切片中!返回出去!
copy(mergedHandlers, group.Handlers)
copy(mergedHandlers[len(group.Handlers):], handlers)
return mergedHandlers
}
那么就没有办法正常执行剩下的中间件了!
c.Set()/c.Get()
c.Set()
和c.Get()
这两个方法多用于在多个函数之间通过c
传递数据的,比如我们可以在认证中间件中获取当前请求的相关信息(user信息等)通过c.Set()
存入c
,然后在后续处理业务逻辑的函数中通过c.Get()
来获取当前请求的用户。c
就像是一根管道,将该次请求相关的所有的函数都串起来了。
评论区