Gin框架介绍
Gin是一个用 Go (Golang) 编写的 web 框架。它是一个类似于martini但性能更好的API框架,不同于谢大主导的Beegoweb框架,后者更像是Python语言中的Django
框架,内部包含了开发一个web程序所需的各种组件。
如果你是性能和高效的追求者,我相信你会像我一样爱上Gin。
同时,不同于其他Golang语言的API框架,该框架社区十分活跃,并且主程仍然在不断更新和改进,我们知道通常情况下在选择一个开源的软件或者相关库时,社区的活跃度以及项目的更新情况会非常重要(考虑到后期的维护和性能和特性问题)。
另外一方面,该框架官方提供了很多简单的示例来供我们快速运行一个期望的http服务,这对于一个刚转入Golang进行业务开发的同学来说是一个非常好的开始。
当然我在很长一段时间也仅是去看官方示例来快速熟悉并实现自己的业务需求,但当有一些特殊的需求时通常去查看官方的具体实现来满足需求,长期如此,不仅耗时且效率极低,因此我产生了将核心源码探究一番的兴趣,希望能通过源码方式来对Gin框架有个深入的学习。
Gin框架中的几个核心结构
我们都知道开发一个HTTP服务,首先需要启动一个TCP监听,然后需要有一些列的handler来处理具体的业务逻辑,最后在再将具体的业务逻辑通过HTTP协议约定和相关的Method和URL进行绑定,以此来对外提供具体功能的HTTP服务。那么在Gin框架对应的就是如下几个模型,我们将一起学习Gin的实现。
Gin框架中的几个重要的模型:
- Engine: 用来初始化一个
gin
对象实例,在该对象实例中主要包含了一些框架的基础功能,比如日志,中间件设置,路由控制(组),以及handlercontext等相关方法.源码文件 - Router: 用来定义各种路由规则和条件,并通过HTTP服务将具体的路由注册到一个由context实现的handler中
- Context:
Context
是框架中非常重要的一点,它允许我们在中间件间共享变量,管理整个流程,验证请求的json以及提供一个json的响应体. 通常情况下我们的业务逻辑处理也是在整个Context引用对象中进行实现的. - Bind: 在Context中我们已经可以获取到请求的详细信息,比如HTTP请求头和请求体,但是我们需要根据不同的HTTP协议参数来获取相应的格式化数据来处理底层的业务逻辑,就需要使用
Bind
相关的结构方法来解析context中的HTTP数据
1.Gin框架中的Engine结构体
当我们在使用框架[Gin](https://github.com/gin-gonic/gin)
来创建一个HTTP服务时,首先我们需要初始化一个实例,在Engine
结构体中就包含了实例的一些基本属性和实例化的一些方法。
Engine结构体
:
|
|
HandlerFunc定义
:
|
|
初始化Engine的方式
:
New()
: 该函数返回一个默认的Engine引用实例(开启了自动重定向,转发客户端ip和禁止请求路径转义)Default()
: 内部调用New()
函数,但是增加了Logger和Recovery两个中间件
Engine对外常用的方法
:
Delims(left, right string) *Engine
: 给创建好的gin实例指定模板引擎的左右分割符SecureJsonPrefix(prefix string) *Engine
: 给创建好的gin实例设置secureJsonPrefixiSetHTMLTemplate(templ *template.Template)
: 该方法会gin实实例绑定一个模板引擎(内部其实是设置了engine的HTMLRender属性)LoadHTMLGlob(pattern string)
: 该方法用来加载glob模式(类似于shell中的正则)的html模板文件,然后将结果和HTML模板引擎关联(内部调用SetHTMLTemplate
方法将全部匹配到模板注册进去)LoadHTMLFiles(files ...string)
: 该方法用上,需要指定一组模板文件名SetFuncMap(funcMap template.FuncMap)
: 该方法会设置一个FuncMap给template.FuncMap使用(内部其实设置了engine的FuncMap)NoRoute(handlers ...HandlerFunc)
: 该方法为NoRoute增加一些handler,它默认会返回404(通常在企业里,404我们会处理的比较优雅一些,比如给一些企业的静态页啥的)NoMethod(handlers ...HandlerFunc)
: 同上,该方法用于给NoMethod增加handler,默认返回405Use(middleware ...HandlerFunc) IRoutes
: 该方法用于绑定一个全局的中间件给router. 通过该方法注册的中间件将包含在每个请求的handler chain中(比如可以在这里使用一些logger或者error相关的中间件). 在上面初始化实例的Default()
函数中其实内部使用了engine.Use(Logger(), Recovery())
来加载logger和recovery中间件Routes() (routes RoutesInfo)
: 该方法用来返回一个路由列表信息RoutesInfo(一个路由信息RouteInfo中包含Method,Path,Handler,HandlerFunc),该方法底层调用engine的trees来获取一些router必要的信息.Run(addr ...string) (err error)
: 该方法会绑定router到http.Server中并开启一个http监听来接收http请求. 该方法其实是http.ListenAndServe(addr, engine)
的简单实现. 注意:该方法除非出现错误,否则会无期限阻塞调用goroutine来接收请求(engine内部只要实现了http.ServeHTTP方法即可)RunTLS(addr, certFile, keyFile string) (err error)
: 同上,以https方式运行服务RunUnix(file string) (err error)
: 同Run(addr)
方法,通过指定的unix socket文件运行服务RunFd(fd int) (err error)
: 同Run(addr)
方法,通过指定的文件描述符(fd)来运行服务RunListener(listener net.Listener) (err error)
: 同Run(addr)
,通过制定的net.Listener
来运行服务ServeHTTP(w http.ResponseWriter, req *http.Request)
: 该方法遵循了http.Handler
的接口规范,可使gin内部调用http.ListenAndServe
来启动一个http服务HandleContext(c *Context)
: 该方法会重新确认一个被重写的context(可以通过c.Request.URL.Path来实现). 需要注意的是该方法可能造成context的循环使用(会绕死你,谨慎使用)
2.Gin框架中的Router
使用Engine
结构体中提供的相关方法,我们就可以快速的启动一个HTTP服务了,但是如何对外暴露一个URL来简单实现一个HTTP的数据传输呢,这个时候就需要使用Router中的方法了。
Gin框架中Router相关的结构体
:
RouterGroup
: 该结构体被用来在Gin内部配置一个路由,一个RouterGroup被用来关联URL前缀和一组具体的handler业务逻辑IRoutes
: IRoutes是一个定了了所有路由处理的接口(包含一些常用的HTTP方法)IRouter
: IRouter则是一个包含单个路由和路由组的所有路由处理的接口
RouterGroup相关结构定义
:
|
|
还记得在上一节中我们的Engine
结构体中有一个RouterGroup
字段吗,该字段会在我们创建一个Engine
的gin实例后帮助我们初始化一个默认的RouterGroup
实例。
比如在Engine
结构体的中的New()
函数,会初始化一个带有如下RouterGroup
的gin实例,并将gin实例注册到RouterGroup的engine
字段.源码文件
|
|
RouterGroup结构体对外暴露的常用方法
:
Use(middleware ...HandlerFunc) IRoutes
: 注册一个中间件并返回Iroutes接口Group(relativePath string, handlers ...HandlerFunc) *RouterGroup
: Group方法会创建一个新的路由组。通常我们会创建一个公共的中间件或者是具有相同前缀的路由,来归并到一个路由组BasePath() string
: 该方法用来返回一个路由组初始路径(比如 v := router.Group("/rest/n/v1/api"),则v.BasePath()就是"/rest/n/v1/api")Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes
: 该方法会使用给定的HTTP方法和URL来注册一个新的handler。(最后一个handler应该是真正的处理程序,其他的应该是在不同的路由之间共享的中间件)。注意:内部调用了一个handle(httpMethod, relativePath string, handlers HandlersChain)的私有方法来处理核心逻辑
POST(relativePath string, handlers ...HandlerFunc) IRoutes
: 该方法是router.Handle("POST", path, handle)
的快速实现,GET(relativePath string, handlers ...HandlerFunc) IRoutes
: 同上DELETE(relativePath string, handlers ...HandlerFunc) IRoutes
: 同上PATCH(relativePath string, handlers ...HandlerFunc) IRoutes
: 同上PUT(relativePath string, handlers ...HandlerFunc) IRoutes
: 同上OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes
: 同上HEAD(relativePath string, handlers ...HandlerFunc) IRoutes
: 同上Any(relativePath string, handlers ...HandlerFunc) IRoutes
: 同上,会将HTTP的所有方法都注册上去StaticFile(relativePath, filepath string) IRoutes
: 该方法用来注册一台路由来服务本地文件系统的单个文件,比如:router.StaticFile("favicon.ico", "./resources/favicon.ico")
Static(relativePath, root string) IRoutes
: 该方法用来提供一个指定文件系统根路径的的路由,内部调用group.StaticFS(path,Dir(root,false))
来提供服务StaticFS(relativePath string, fs http.FileSystem) IRoutes
: 指定文件系统(http.FileSystem)来创建一个服务
3.Gin实例示例
有了上面两个核心模型Engine
和RouteGroup
的了解,此时我们就可以通过Gin框架快速来创建一个简单HTTP服务了。
1.默认路由
|
|