目 录CONTENT

文章目录
Go

gin框架的中间件机制介绍

Hello!你好!我是村望~!
2023-02-13 / 0 评论 / 2 点赞 / 169 阅读 / 958 字
温馨提示:
我不想探寻任何东西的意义,我只享受当下思考的快乐~

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

image-20230213082639026

相关常用方法

c.Next()

func (c *Context) Next() {
	c.index++
	for c.index < int8(len(c.handlers)) {
		c.handlers[c.index](c)
		c.index++
	}
}

从上面的代码可以看到,这里通过索引遍历HandlersChain链条,从而实现依次调用该路由的每一个函数(中间件或处理请求的函数)。

image-20230213083432721

我们可以在中间件函数中通过再次调用c.Next()实现嵌套调用(func1中调用func2;func2中调用func3),

image-20230213083516190

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就像是一根管道,将该次请求相关的所有的函数都串起来了。

image-20230213083712280
2

评论区