Engine是Gin的根入口,是整个框架的基础。包含了context、tree、RouterGroup相关核心组件。
我们通过 Engine 对象来定义服务路由信息、组装插件、运行服务。
一、结构
代码位置:https://github.com/gin-gonic/gin/blob/master/gin.go。gin.go是真个Gin框架的核心。
1 | type Engine struct { |
- RouterGroup:路由组,在实际开发过程中我们通常会使用路由组来组织和管理一些列的路由,负责存储所有的中间件,包括请求路径。
- RedirectTrailingSlash: 如果当前路由没有匹配到,但是存在不带/开头的handler就会重定向.对于后缀为/的路由是否开启重定向请求,如请求/foo/但仅存在/foo的路由,则客户端使用http状态吗307重定向到/foo进行请求
- RedirectFixedPath:如果开启该参数,没有handler注册时,路由会尝试自己去修复当前的请求地址。
1、首位多余元素会被删除(../ or //);
2、然后路由会对新的路径进行不区分大小写的查找;
3、如果能正常找到对应的handler,路由就会重定向到正确的handler上并返回301或者307.(比如: 用户访问/FOO 和 /..//Foo可能会被重定向到/foo这个路由上) - HandleMethodNotAllowed:如果开启该参数,当当前请求不能被路由时,路由会自己去检查其他方法是否被允许.在这种情况下会响应”Method Not Allowed”,并返回状态码405; 如果没有其他方法被允许,将会委托给NotFound的handler
- ForwardedByClientIP:是否转发客户端ip
- RemoteIPHeaders:在以下情况下获取客户端IP。 ForwardedByClientIP为true;context.Request.RemoteAddr 有值
- TrustedProxies:网络来源列表(IPv4地址,IPv4 CIDR,IPv6地址或信任包含以下内容的请求标头的IPv6 CIDR:当(* gin.Engine).ForwardedByClientIP为true
- AppEngine:如果开启将会在请求中增加一个以”X-AppEngine…”开头的header
- CloudflareProxy:如果启用,它将信任cf - connections -IP头来确定客户端的IP
- UseRawPath:如果开启将会使用url.RawPath去查找参数(默认:false)
- UnescapePathValues:如果为true,则将不转义路径值。如果UseRawPath为false(默认情况下),则UnescapePathValues实际上为true,作为url.Path将被使用,它已经被转义了
- MaxMultipartMemory:赋予http.Request的ParseMultipartForm的’maxMemory’参数的值。
- RemoveExtraSlash:是否删除额外的反斜线(开始时可解析有额外斜线的请求)
- delims:Delims代表用于HTML模板呈现的一组左右定界符(render.Delims表示使用HTML渲染的一组左右分隔符,具体可见html/template库)
- secureJSONPrefix:设置在Context.SecureJSON中的json前缀
- HTMLRender:返回HTMLRender接口(用于渲染HTMLProduction和HTMLDebug两个结构体类型的模板)
- FuncMap:html/template包中的FuncMap map[string]interface{} ,用来定义从名称到函数的映射
- allNoRoute:复制一份全局的handlers,加上NoRoute处理方法
- allNoMethod:复制一份全局的handlers,加上NoMethod处理方法
- noRoute:noRoute为NoRoute添加处理程序。它默认返回404代码
- noMethod:noMethod为NoMethod添加处理程序,它默认返回405代码
- pool:主要用来存储context上下文对象,用来优化处理http请求时的性能(参见sync.Pool)的介绍)
- trees:负责存储路由和handle方法的映射,采用的是Radix树的结构(参见Gin框架学习(四)–Trees)
- maxParams:分配context时,初始化参数Params的切片长度(methodTree是一个包含请求方法和node指针的结构体,node是一个管理path的节点树)
- trustedCIDRs:IP网络地址指针切片
二、主要方法介绍
1 | New() |
New()
1 | func New() *Engine { |
New()方法返回一个空的Engine对象,并且没有挂载任何的中间件。在该方法中对部分参数进行了初始值的设置(开启了自动重定向,转发客户端ip和禁止请求路径转义等)。RouterGroup.engine字段指向了当前Engine对象。并初始化了pool.New方法。
Default()
1 | func Default() *Engine { |
Default()内部调用New()函数,但是增加了Logger和Recovery两个中间件
New()和Default()两个方法提供了两种构造Engine的通用方法。
allocateContext()
1 | func (engine *Engine) allocateContext() *Context { |
allocateContext就是初始化pool对象的方法(pool参见sync.Pool)的介绍)
NoRoute(handlers …HandlerFunc)
1 | func (engine *Engine) NoRoute(handlers ...HandlerFunc) { |
同NoRoute,该方法用于给NoMethod增加handler,默认返回405
Use(middleware …HandlerFunc) IRoutes
1 | func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { |
该方法用于绑定一个全局的中间件给router. 通过该方法注册的中间件将包含在每个请求的handler chain中(比如可以在这里使用一些logger或者error相关的中间件). 在上面初始化实例的Default()函数中其实内部使用了engine.Use(Logger(), Recovery())来加载logger和recovery中间件
其中IRouter是一个定义各种Handler方法的接口,代码如下。
1 | type IRouter interface { |
其关系如下图所示:
addRoute(method, path string, handlers HandlersChain)
1 | func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { |
注册Handler的方法,匹配路径与handler。具体逻辑会在tree(addRoute)中详细介绍。
Routes() (routes RoutesInfo)
1 | func (engine *Engine) Routes() (routes RoutesInfo) { |
该方法用来返回一个路由列表信息RoutesInfo(一个路由信息RouteInfo中包含Method,Path,Handler,HandlerFunc),该方法底层调用engine的trees来获取一些router必要的信息.
Run(addr …string) (err error)
1 | func (engine *Engine) Run(addr ...string) (err error) { |
该方法会绑定router到http.Server中并开启一个http监听来接收http请求. 该方法其实是http.ListenAndServe(addr, engine)的简单实现. 注意:该方法除非出现错误,否则会无期限阻塞调用goroutine来接收请求(engine内部只要实现了http.ServeHTTP方法即可).
该方法执行后,gin模块启动开始对外提供服务。
ServeHTTP(w http.ResponseWriter, req *http.Request)
1 | func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { |
该方法遵循了http.Handler的接口规范,可使gin内部调用http.ListenAndServe来启动一个http服务。ServeHTTP方法就是每次http请求在gin引擎的入口。
- 从pool中获取一个Context对象(pool参见gin.Context)的介绍
- 初始化Context包括Request对象
- 调用handleHTTPRequest方法执行对应的handler(具体如下面handleHTTPRequest的介绍)
- handler执行完毕将Context对象放回pool中
handleHTTPRequest(c *Context)
1 | func (engine *Engine) handleHTTPRequest(c *Context) { |
1 | func serveError(c *Context, code int, defaultMessage []byte) { |
handleHTTPRequest就是每次请求的核心流程。
遍历路由树中对应的httpMethod的节点
找到path对应的路由节点(后续会在tree中详细介绍),并Params赋值
c.Next()执行路由对应的处理方法
填充响应头,致此如果执行顺利则结束并返回
如果出现不规则的自定义路由时并且有类似的路由存在。重定向处理
检查是否允许使用其他方法(get、post等)
检查到该请求方式没有请求被路由时,返回不允许的方法(http code = 405)
如果没有路由则返回http code = 404
最后进行404错误处理(serveError方法)
三、请求链路
一次HTTP请求调用链路如下图(没有任何middleware)。
本章介绍了Engine,部分细节需要在tree(Trees)中详细介绍。