基本示例
basic
main
func main() {
r := setupRouter()
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}
这部分代码简单、直接,创建好路由后,直接在端口 8080 开启服务。
r := gin.Default()
setupRouter
创建 gin.Engine 结构,gin.Engine 满足 gin.IRouter 接口
r := gin.Default()
添加 /ping 路由,普通路由,没有参数
r.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "pong")
})
添加带参数路由
r.GET("/user/:name", func(c *gin.Context) {
user := c.Params.ByName("name")
value, ok := db[user]
if ok {
c.JSON(http.StatusOK, gin.H{"user": user, "value": value})
} else {
c.JSON(http.StatusOK, gin.H{"user": user, "status": "no value"})
}
})
添加路由组
authorized := r.Group("/", gin.BasicAuth(gin.Accounts{
"foo": "bar", // user:foo password:bar
"manu": "123", // user:manu password:123
}))
authorized.POST("admin", func(c *gin.Context) {
user := c.MustGet(gin.AuthUserKey).(string)
// Parse JSON
var json struct {
Value string `json:"value" binding:"required"`
}
if c.Bind(&json) == nil {
db[user] = json.Value
c.JSON(http.StatusOK, gin.H{"status": "ok"})
}
})
从基本示例中,可以看到 gin 的一些基本特性
- 支持路由分组
- 支持参数验证
从以上基础,即可展开分析。
源码分析
全局概览
从上图,结合代码,可以知道 gin 框架的基本关系。gin.Engine 实现 http.Handler 接口,做为 http.Server 的参数接入 http 模块儿。内部的 IRouter 接口,负责路由管理,IRouter 的 Group 方法会生成一个新结构体 RouterGroup,RouterGroup 实现 IRouter 与 IRoutes 接口, RouterGroup 可通过 engine 属性,找到其挂载的 Engine 结构。
再看一下 Engine 结构定义
type Engine struct {
RouterGroup
// ...
}
RouterGroup 结构实现了 IRoutes 接口来管理路由。Engine 匿名包含 RouterGroup 结构,也自然的实现了 IRouter 与 IRoutes 接口。
Group
因为匿名包含,Engine 的 Group 方法会直接使用 RouterGroup 的 Group 方法。执行过程如下图所示:
Use
Use 方法为 IRoutes 接口中方法,有两条可能的执行路径,一个是在 Engine 直接关联的 RouterGroup 上挂载,一个是在 Engine.Group() 生成的新的 RouterGroup 上挂载。
路由概览
相关数据结构示意图
从上图看,gin.node 是一个树状结构,以下图为例
GET 添加路由
Engine 添加路由时,会调用 RouterGroup 中对应方法,且 RouterGroup 可直接添加路由。RouterGroup 在添加路由时,通过请求方法,找到对应的路由树,通过路由树构建的算法,找到合适位置插入新节点。插入新节点时,将 RouterGroup 的全部 Handler 方法,直接复制到新节点中。