Gin
是使用Go语言编写的高性能的web
服务框架,根据官方的测试,性能是httprouter
的40倍左右。要使用好这套框架呢,首先我们就得对这个框架的基本结构有所了解,所以我将从以下几个方面来对Gin
的源码进行解读。
- 第一章:
Gin
是如何储存和映射URL
路径到相应的处理函数的 - 第二章:
Gin
中间件的设计思想及其实现 - 第三章:
Gin
是如何解析客户端发送请求中的参数的 - 第四章:
Gin
是如何将各类格式(JSON/XML/YAML
等)数据解析返回的
Gin是如何组织和映射URL到处理函数的
在谈这个之前我们先以官方给的示例代码来作为我们的切入口,然后一步一步的看Gin
是如何处理的,了解Gin
的深层次工作原理。
func main() {
router := gin.Default()
// 这个处理函数将会匹配 /user/john 但不会匹配 /user/ 或者 /user
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
// 这个路径将会匹配 /user/john/ 或者 /user/john/send
// 如果没有匹配到 /user/john, 那么将会被重定向到 /user/john/
router.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
message := name + " is " + action
c.String(http.StatusOK, message)
})
router.POST("/user/:name/*action", func(c *gin.Context) {
log.Println(c.FullPath() == "/user/:name/*action") // true
})
router.Run(":8080")
}
首先,我们要通过gin.Default()
获取到一个Engine
对象指针,然后由router
来负责将各个映射路径记录到处理的映射关系。我们先来看下Engine
的数据结构是怎样的。
type Engine struct {
//中间件信息就存储在这个里面
RouterGroup
//是否启动自动重定向。例如:配置Handler时是/foo,
//但实际发送的请求是/foo/。在启用本选项后,将会重定向到/foo
RedirectTrailingSlash bool
// 是否启动请求路由修复功能。
// 启用过后,当/../foo找不到匹配路由时,会自动删除..部分路由,然后重新匹配知道找到匹配路由,如上路由就会被匹配到/foo
RedirectFixedPath bool
//启用后,如果找不到当前路由匹配的HTTP方法,
//而在其他HTTP方法中能找到,则返回405响应码。
HandleMethodNotAllowed bool
//是否获取真正的客户端IP,而不是代理服务器IP(nginx等),
// 开启后将会从"X-Real-IP和X-Forwarded-For"中解析得到客户端IP
ForwardedByClientIP bool
//启用后将在头部加入"X-AppEngine"标识,以便与PaaS集成
AppEngine bool
//启用后,将使用原有的URL.rawPath(没有对转义字符进行处理的,
// 如%/+等)地址来进行解析,而不是使用URL.path来解析,默认为false
UseRawPath bool
//如果启用,则路径中的转义字符将不会被转义
UnescapePathValues bool
//设置用来缓存客户端发送的文件的缓冲区大小,默认:32MB
MaxMultipartMemory int64
//启用后将会删除多余的分隔符"/"
RemoveExtraSlash bool
//用于保存tmpl文件中用于引用变量的定界符,默认是"{
{}}",
// 调用r.Delims("{[{", "}]}")可以修改
delims render.Delims
//设置防止JSON劫持,在json字符串前加的逻辑代码,
//默认是:"while(1);"
secureJsonPrefix string
//html文件解析器
HTMLRender render.HTMLRender
//tmpl文件的内建函数列表,可以在tmpl文件中调用函数,使用
//router.SetFuncMap(template.FuncMap{
// "formatAsDate": formatAsDate,
//})可设置
FuncMap template.FuncMap
// HandlersChain就是func(*Context)数组
// 以下四个调用链中保存的就是在不同情况下回调的处理函数
// 找不到匹配路由(404)
allNoRoute HandlersChain
//返回405状态时会回调
allNoMethod HandlersChain
//没有配置路由时回调,主要是代码测试时候使用的
noRoute HandlersChain
//没有配置映射方法时回调,主要是代码测试时候使用的
noMethod HandlersChain
//连接池用于保存与客户端的连接上下文(Context)
pool sync.Pool
//路径搜索树,代码中配置的路由信息都以树节点的形式组织起来
// 下面会详细介绍
trees methodTrees
}
在大致了解完Gin
的核心对象Engine
之后,我们脑海里就可以有一个大致的结构了。因为整个框架都是为绕着Engine
来进行编写的,下面我们就重点介绍一下Engine
中的几个比较重要的结构,来更深入地了解Gin
。
RouterGroup
type RouterGroup struct {
Handlers HandlersChain
basePath string
//注意这里存在交叉依赖
engine *Engine
root bool
}
光看这个RouterGroup
结构体,我们仿佛还不太清楚他的作用是什么,不过我们通过一下几个方法就可以知道了。
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
从这里函数一看,咦?这不就是绑定中间件方法吗?从这里我们就可以看出RouterGroup
的Handlers
是用于储存中间件函数的。除此之外,RouterGroup
还实现了IRoutes
接口。