Gin中间件工作流程
Gin是Go语言中一个很流行的Web框架,其性能比较高,比较简洁,中间件与Koa类似,采用了洋葱模型,我们来看一下它的大概执行原理。
中间件使用方式
中间件使用方式示例:
1 | // 自定义中间件Logger |
洋葱模型实现
执行流程示例
先给出一个示例,然后我们对照该实例去实现它。
1 | func Middleware1(c *gin.Context) { |
可见我们定义了三个中间件和一个处理函数,在请求/时触发的流程为:
1 | middleware1 before |
符合洋葱模型的执行流程
代码实现
我们只会实现其大致流程,不关注细节。
首先是Context的定义:
1 | // Context的基本定义,忽略无关属性 |
收集流程涉及太多其他内容,不在这里赘述,我们只需要清除是先收集中间件,后将处理函数加入到handlers的结尾并手动调用Next方法。
如上示例中,handlers该有以下内容:
1 | [Middleware1, Middleware2, Middleware3, HandlerFunc] |
实现洋葱模型的核心就是Next()方法,我们看一下它的实现:
1 | func (c *Context) Next() { |
实现就是这么简单,与Koa一样,实现方式很简洁,根据上边的示例我们可以想一下它的执行流程:
- 执行
Middleware1中的before部分 - 执行
Middleware1中的Next() index++后执行Middleware2中的before部分for循环继续运行执行Middleware3中的before部分- 执行
Middleware3中的Next() index++后执行HandlerFuncfor循环已经跳出,执行调用栈中Middleware3的after部分- 执行调用栈中
Middleware1的after部分
执行流程一致。
主要有两个细节需要注意:
- 为什么要执行
for循环?
因为不是所有的中间件都需要在请求处理函数后做一些操作,那么也就不是所有的中间件内部都会执行
Next()方法。所以需要加一个循环,即便不调用
Next()也会依次执行所有中间件
- 执行第一个中间件调用
Next开始for循环,执行后续中间件中的Next又会开始for循环,会不会导致后续的中间件重复执行?
并不会。
因为我们修改的是
c.index,而c是*Context,所以在所有中间件中使用的是同一个Context,其index值的修改会影响到所有中间件,并不会重复执行。