Gin源码分析 – Engine结构体

Engine源码结构体

当我们在使用Gin框架来创建一个HTTP服务时,首先我们需要初始化一个实例,在Engine结构体中就包含了实例的一些基本属性和实例化的一些方法。

Engine 对象是 gin 的框架实例,它其中包括了路由定义以及一些配置相关的参数:

  • RouterGroup:管理路由和中间件的组件,它定义了 URL 路径与处理函数的映射关系。
  • RedirectTrailingSlash:如果当前路径的处理函数不存在,但是路径+’/’的处理函数存在,则允许进行重定向,默认为 true。
  • RedirectFixedPath:允许修复当前请求路径,如/FOO和/..//Foo会被修复为/foo,并进行重定向,默认为 false。
  • UseRawPath:使用未转义的请求路径(url.RawPath),默认为 false。
  • UnescapePathValues:对请求路径值进行转义(url.Path),默认为 true。
  • RemoveExtraSlash:去除额外的反斜杠,默认为 false。
  • trees:每一个 HTTP 方法会有一颗方法树,方法树记录了路径和路径上的处理函数。
type Engine struct {
  // 路由组,在实际开发过程中我们通常会使用路由组来组织和管理一些列的路由. 
  // 比如: /apis/,/v1/等分组路由
  RouterGroup
  // 开启自动重定向。如果当前路由没有匹配到,但是存在不带/开头的handler就会重定向.
  // 比如: 用户输入/foo/但是存在一个/foo 就会自动重定向到该handler,
  // 并且会向客户端返回301或者307状态码(区别在于GET方法和其他方法)
  RedirectTrailingSlash bool
  // 如果开启该参数,没有handler注册时,路由会尝试自己去修复当前的请求地址.
  // 修复流程:
  // 1.首位多余元素会被删除(../ or //); 
  // 2.然后路由会对新的路径进行不区分大小写的查找;
  // 3.如果能正常找到对应的handler,路由就会重定向到正确的handler上并返回301或者307.
  // (比如: 用户访问/FOO 和 /..//Foo可能会被重定向到/foo这个路由上)
  RedirectFixedPath bool
  // 如果开启该参数,当当前请求不能被路由时,路由会自己去检查其他方法是否被允许.
  // 在这种情况下会响应"Method Not Allowed",并返回状态码405; 
  // 如果没有其他方法被允许,将会委托给NotFound的handler
  HandleMethodNotAllowed bool
  // 是否转发客户端ip
  ForwardedByClientIP    bool
  // 如果开启将会在请求中增加一个以"X-AppEngine..."开头的header
  AppEngine bool
  // 如果开启将会使用url.RawPath去查找参数(默认:false)
  UseRawPath bool
  // 如果开启,请求路径将不会被转义.
  // 如果UseRawPath为false,该参数实际上就为true(因为使用的是url.Path)
  UnescapePathValues bool
  // maxMemory参数的值(http.Request的ParseMultipartForm调用时的参数)
  MaxMultipartMemory int64
  // 是否删除额外的反斜线(开始时可解析有额外斜线的请求)
  RemoveExtraSlash bool
  // 分隔符(render.Delims表示使用HTML渲染的一组左右分隔符,具体可见html/template库)
  delims           render.Delims
  // 设置在Context.SecureJSON中国的json前缀
  secureJsonPrefix string
  // 返回一个HTMLRender接口(用于渲染HTMLProduction和HTMLDebug两个结构体类型的模板)
  HTMLRender       render.HTMLRender
  // html/template包中的FuncMap map[string]interface{} ,
  // 用来定义从名称到函数的映射
  FuncMap          template.FuncMap
  // 以下是gin框架内部定义的一些属性
  // HandlersChain 是一个HandlerFunc 的数组
  // HandlerFunc其实就是一个Context的指针
  allNoRoute       HandlersChain
  allNoMethod      HandlersChain
  noRoute          HandlersChain
  noMethod         HandlersChain
  // 这里定义了一个可以临时存取对象的集合
  // (sync.Pool是线程安全的,主要用来缓存为使用的item以减少GC压力,
  // 使得创建高效且线程安全的空闲队列)
  pool             sync.Pool
  // methodTrees是methodTree的切片
  // (methodTree是一个包含请求方法和node指针的结构体,node是一个管理path的节点树)
  trees            methodTrees
}

HandlerFunc定义:

// 定义了一个可以被中间件使用的handler
type HandlerFunc func(*Context)

初始化Engine的方式

  • New(): 该函数返回一个默认的Engine引用实例(开启了自动重定向,转发客户端ip和禁止请求路径转义)
  • Default(): 内部调用New()函数,但是增加了Logger和Recovery两个中间件

Engine对外常用的方法

  • Delims(left, right string) *Engine: 给创建好的gin实例指定模板引擎的左右分割符
  • SecureJsonPrefix(prefix string) *Engine: 给创建好的gin实例设置secureJsonPrefixi
  • SetHTMLTemplate(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,默认返回405
  • Use(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的循环使用(会绕死你,谨慎使用)