一、Gin框架的路由原理:
参考:
首先了解下什么是路由?
简而言之,http路由即是一条http请求的“向导”,根据URI上的路径,指引该条请求到对应的方法里去执行然后返回,中间可能会执行一些中间件。其次,路由又分为 静态路由,动态路由…
- 静态路由: 框架/用户提前生成一个路由表,一般是map结构,key为URL上的path,value为代码执行点(处理函数),
- 优点:只需要读取map,没有任何开销,速度快
- 缺点:无法正则匹配路由,只能逐一对应,模糊匹配的场景无法使用
- 动态路由: 用户定义好路由匹配规则,框架匹配路由时,根据规则动态的去规划路由
- 优点:适应性强,解决了静态路由的缺点
- 缺点:相比静态路由有开销,具体视算法和路由匹配规则而定
gin框架作为一个轻量级的web框架,采用的是字典树(前缀树)的方式实现的动态路由,不支持路由的正则匹配。当 gin 注册路由时,会根据不同的 Method 分别注册不同的路由树,比如 这四个请求,会分别注册四棵路由树出来:
GET /user/{userID} HTTP/1.1
POST /user/{userID} HTTP/1.1
PUT /user/{userID} HTTP/1.1
DELETE /user/{userID} HTTP/1.1
gin路由注册的示例代码及其前缀树示意图,分别如下:
r := gin.New()
r.GET("/user/:name", routeUser)
func routeUser(c *gin.Context){
//todo something
}
/,/bear,/bell,/bid,/bull,/buy,/sell,/stock,/stopseu
二、Gin框架的中间件原理(Middleware)
参考:
中间件是为了过滤路由而发明的一种机制,也就是http请求来到时先经过中间件,再到具体的处理函数。
首先,Gin框架的中间件是基于洋葱模型的,如下图:
beforeFunc1和afterFunc1即是中间件1;afterFunc2和afterFunc2即是中间件2。
过程:请求到来时从最外层开始执行中间件1,然后进入第二层,依次执行完所有中间件最后到达主体函数,接着再一层一层的往外走再次执行中间件2…中间件1…最后返回,也有点像栈的概念。
其次,再来看下gin.Context的结构体的主要字段:
// gin.Context 结构体
type Context struct {
...
handlers HandlersChain // 函数指针切片对象
index int8 // 对应函数指针切片中的索引下标,执行c.Next()时会向后移动index下标位置
...
}
type HandlerFunc func(*Context) // 函数指针
type HandlersChain []HandlerFunc // 函数指针切片
r := gin.New()
r.Use()
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
c.Next()
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
c.Abort()
func (c *Context) Abort() {
c.index = abortIndex
}
因此,gin框架的 [中间件函数/路由处理函数] 实际上都是以切片的形式的调用链条存在的(本质上就是函数指针切片),我们可以顺序调用也可以借助 c.Next() 方法实现嵌套调用。
三、总结
- gin框架路由使用字典树(前缀树),路由注册的过程是构造前缀树的过程,路由匹配的过程就是查找前缀树的过程。另外,gin框架不支持路由的正则匹配。
- gin框架的 [中间件函数和处理函数] 是以切片的形式的调用链条存在的(本质上就是函数指针切片),我们可以顺序调用也可以借助 c.Next() 方法实现嵌套调用。
- 借助c.Set()和c.Get()方法我们能够在不同的中间件函数中传递数据。