在《Gin源码解析和例子——路由》一文中,我们已经初识中间件。本文将继续探讨这个技术。(转载请指明出于breaksoftware的csdn博客)

Gin的中间件,本质是一个匿名回调函数。这和绑定到一个路径下的处理函数本质是一样的。

再以Engine的Default方法为例

func Default() *Engine {debugPrintWARNINGDefault()engine := New()engine.Use(Logger(), Recovery())return engine
}

第4行就让该Engine使用了Logger和Revoery两个中间件。Use方法将新增的中间件加入到中间件集合中

// Use adds middleware to the group, see example code in github.
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {group.Handlers = append(group.Handlers, middleware...)return group.returnObj()
}

因为是append,所以后加入的中间件排在集合后面。理解这个特性对我们正确使用中间件很重要。

再回顾下之前介绍的路由的代码

 r := gin.Default()// Ping testr.GET("/ping", func(c *gin.Context) {c.String(http.StatusOK, "pong")})

host:port/ping下的请求,将被路由到输出pong的匿名函数里。GET方法封装了handle方法

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {absolutePath := group.calculateAbsolutePath(relativePath)handlers = group.combineHandlers(handlers)group.engine.addRoute(httpMethod, absolutePath, handlers)return group.returnObj()
}

这儿注意下第3行,上面这个匿名函数似乎是和其他匿名函数合并成一个匿名函数集合。然后再在第4行和绝对路径绑定。

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

这儿合并的就是中间件集合(group.Handlers)。第7~8行代码,告诉我们中间件的回调要先于用户定义的路径处理函数。那么上例中,mergeHandlers中的成员是【logger回调,recovery回调,GET的匿名回调】。

这样,每个路径的回调函数链都将包含中间件的回调,即【logger回调,recovery回调】。

我再看一个最简单的中间件的实现

func MiddlewareDemo() gin.HandlerFunc {return func(c *gin.Context) {c.Next()}
}

这个中间件只是返回了一个匿名函数,该函数内部需要调用Conext的Next函数来驱动执行之后的handler。

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

这也是Gin设计中比较奇葩的地方:

  • Context的Next方法让合并之后的handlers中的回调执行
  • handlers中的回调调用Context的Next方法以驱动下个回调执行

如果我们不看Next的实现,单从上面的话中可以感觉到似乎逻辑进入了一种异常循环的状态。其实Gin使用了一个Context中的index变量来解决了这个问题。于是中间件、框架和路径对应的回调之前的关系是

我们看个例子

package mainimport ("log""net/http""github.com/gin-gonic/gin"
)func MiddlewareA() gin.HandlerFunc {return func(c *gin.Context) {log.Println("MiddlewareA before request")// before requestc.Next()// after requestlog.Println("MiddlewareA after request")}
}func MiddlewareB() gin.HandlerFunc {return func(c *gin.Context) {log.Println("MiddlewareB before request")// before requestc.Next()// after requestlog.Println("MiddlewareB after request")}
}// This function's name is a must. App Engine uses it to drive the requests properly.
func main() {// Starts a new Gin instance with no middle-warer := gin.New()r.Use(MiddlewareA(), MiddlewareB())r.GET("/ping", func(c *gin.Context) {c.String(http.StatusOK, "pong")log.Println("pong")})r.Run(":8080")
}

触发一次请求后,服务器的日志输出是

2018/12/03 16:07:30 MiddlewareA before request
2018/12/03 16:07:30 MiddlewareB before request
2018/12/03 16:07:30 pong
2018/12/03 16:07:30 MiddlewareB after request
2018/12/03 16:07:30 MiddlewareA after request

可以看到,结果符合我们对代码的解读。

Gin源码解析和例子——中间件(middleware)相关推荐

  1. Gin源码解析和例子——路由

    Gin是一个基于golang的net包实现的网络框架.从github上,我们可以看到它相对于其他框架而言,具有优越的性能.本系列将从应用的角度来解析其源码.(转载请指明出于breaksoftware的 ...

  2. gin 源码解析 - 详解http请求在gin中的流转过程

    本篇文章是 gin 源码分析系列的第二篇,这篇文章我们主要弄清一个问题:一个请求通过 net/http 的 socket 接收到请求后, 是如何回到 gin 中处理逻辑的? 我们仍然以 net/htt ...

  3. Colly源码解析——结合例子分析底层实现

    通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...

  4. gin源码解析(1) - gin 与 net/http 的关系

    gin是目前Go里面使用最广泛的框架之一了,弄清楚gin框架的原理,有助于我们更好的使用gin.这个系列gin源码阅读会逐步讲明白 gin 的原理,欢迎关注后续文章. gin 概览 想弄清楚 gin, ...

  5. Go框架 gin 源码学习--路由的实现原理剖析

    往期回顾: gin源码解析 - gin 与 net/http 的关系 gin 源码解析 - 详解http请求在gin中的流转过程 上面两篇文章基本讲清楚了 Web Server 如何接收客户端请求,以 ...

  6. Go http源码解析(一)

    Go web之旅 此篇开始将开启Go web之旅,我将这趟旅途分为三个子旅程: 源码解析 框架解读 中间件使用 所以在这趟旅途中我们将领略源码之雄伟,框架之奇艳,中间件之灵秀.在接下来的时间里我会按照 ...

  7. 详细讲解go web框架之gin框架源码解析记录及思路流程和理解

    开篇 首先gin 框架是在 官方提供的net/http标准包进行的相应封装. 那么要想理解gin框架, 就要先懂一些 net/http标准包 的相关知识. 可以参考中文的 文档: https://st ...

  8. .Net Core 中间件之主机地址过滤(HostFiltering)源码解析

    一.介绍 主机地址过滤中间件相当于一个白名单,标记哪些主机地址能访问接口. 二.使用 新建WebAPI项目,修改Startup中的代码段如下所示.下面表示允许主机名为"localhost&q ...

  9. Laravel核心解读--Session源码解析

    Session 模块源码解析 由于HTTP最初是一个匿名.无状态的请求/响应协议,服务器处理来自客户端的请求然后向客户端回送一条响应.现代Web应用程序为了给用户提供个性化的服务往往需要在请求中识别出 ...

最新文章

  1. python 词云_python数据可视化——词云
  2. 一个基于 Spring Boot 的项目骨架,少造轮子!
  3. python中concat的用法_python pandas concat用法及代码示例
  4. 滴滴悬赏100万“捉拿”司机;手机QQ又宕机;中兴手机或将出售 | CSDN 极客头条...
  5. Linux下安装Redis讲解
  6. 灰色系统理论的介绍与解释
  7. 【量化金融】利用DCF估值模型实现股票价值监测
  8. 教室录播系统方案_校园录播教室搭建方案?
  9. numpy 相关函数
  10. UCF动作识别的数据集及处理(pytorch)
  11. proteus仿真的过程中,经常会不小心把示波器关掉,导致无法查看波形。那么如何调出示波器呢?
  12. typescript关键词Parameters和ReturnType
  13. 【图解】九张图带你读懂大数据医疗
  14. 【NVMe2.0b 16-1】Get Log Page
  15. shopxo 文件读取(CNVD-2021-15822)
  16. Python数据爬虫学习笔记(10)淘宝图片爬虫实战
  17. 北京邮电大学自考计算机试题及答案,6所北京高校在河南拟招630人 28个自考专业停考...
  18. Parity Game(并查集)
  19. web怎么将dwg转换图片_怎么把CAD图转换成高清jpg图?
  20. 51单片机~串口通信(讲解+代码)

热门文章

  1. Urllib库函数、代理、爬取案例
  2. Linux那些事儿 之 戏说USB(大结局)还是那个match
  3. 你知道吗?Linux命令竟如此简单!
  4. oracle全组件安装,Oracle text组件安装
  5. QT 烦人的parent该如何理解
  6. 强哥原创管理方法论之“掌纹管理学”
  7. 在CentOS 6.6 64bit上基于源码安装全功能的vim 7.4实录
  8. 学习用C#在Unity中创建一个2D Metroidvania游戏
  9. 从头开始学习Unity着色器
  10. Rocksdb 通过ingestfile 来支持高效的离线数据导入