Gin源码阅读-gin.go(施工中-进度70%)

gin.go

从属于package gin。
定义了
	Engine结构体(主要)
及其方法(主要)。

定义了
	HandlersChain []HandlerFunc(用以存储形如HandlerFunc func(*Context)的处理方法)
及其对应的方法
	func (c HandlersChain) Last() HandlerFunc(用以获取“栈”中的具体方法,)

定义了
	RouteInfo结构体
以及对应的slice
	RoutesInfo []RouteInfo

文件头部及全局常量、变量

package gin

import (
	"fmt"
	"html/template"
	"net"
	"net/http"
	"os"
	"path"
	"sync"

	"github.com/gin-gonic/gin/internal/bytesconv"
	"github.com/gin-gonic/gin/render"
)
//此常量用于定义上传文件在内存中可以存储的大小
const defaultMultipartMemory = 32 << 20 // 32 MB

var (
	default404Body   = []byte("404 page not found")
	default405Body   = []byte("405 method not allowed")
	defaultAppEngine bool
)

Engine结构体定义

type Engine struct {
	RouterGroup
	RedirectTrailingSlash bool
	RedirectFixedPath bool
	HandleMethodNotAllowed bool
	ForwardedByClientIP    bool
	AppEngine bool
	UseRawPath bool
	UnescapePathValues bool
	MaxMultipartMemory int64
	RemoveExtraSlash bool
	delims           render.Delims
	secureJsonPrefix string
	HTMLRender       render.HTMLRender
	FuncMap          template.FuncMap
	allNoRoute       HandlersChain
	allNoMethod      HandlersChain
	noRoute          HandlersChain
	noMethod         HandlersChain
	pool             sync.Pool
	trees            methodTrees
}

Engine结构体成员解析

RouterGroup

是一个结构体,定义在routergroup.go文件中。用以配置路由器。
与一组prefix和处理程序(以及middleware)相关联。
type RouterGroup struct {
	Handlers HandlersChain
	basePath string
	engine   *Engine
	root     bool
}

RedirectTrailingSlash bool

如果一个request的path末尾包含了"/",但是没有定义这样的,那么request会被重定向。
GET方法会使用301重定向,其他的方法会使用307重定向。
例如:
向路由"/foo/"发送请求,但是只存在"/foo"这样的路由。那么request会被重定向到"/foo"

RedirectFixedPath bool

开启后,会试图修复没有匹配的requet的path。path中的../和//会被移除。此后,会对request的path进行检查。
如果可以匹配到request所请求的路由,那么会使用301或者307进行重定向到正确的路由。
此选项与RedirectTrailingSlash互不干扰
例如:
/FOO和/..//Foo会被重定向到/foo

HandleMethodNotAllowed bool

当一个request的path无法匹配时,会检查该request的path所请求的路由是否有其他的方法(GET之类的)。
如果存在这样的方法,那么会返回'Method Not Allowed'以及错误码405。
如果没有不存在这个路由,那么会将这个请求发送给NotFound 处理程序

ForwardedByClientIP bool

根据 X-Real-IP 和 X-Forwarded-For头获取真实客户端IP。
可以参考文章:https://www.jianshu.com/p/15f3498a7fad

AppEngine bool

https://github.com/gin-gonic/gin/pull/726
https://github.com/gin-gonic/gin/pull/755
此选项与Google App Engine相关。
开启后,会在添加'X-AppEngine...'这样的头部,以更好的配合Google的GAE(Gin源代码注释的原话)

UseRawPath bool

开启后,会使用url.RawPath对请求的路由进行转义后查找参数。
而url.RawPath会将request的path中如同"%E6%88%91%E6%98%AF%E4%B8%AA%E4%B8%AD"的字符进行转码后,再获取参数。

UnescapePathValues bool

如果开启,那么不会对request的path进行转义。
如果UseRawPath 选项未开启,那么这个选项必然是true。
那么会直接使用url.Path而不是使用url.EscapedPath.MaxMultipartMemory
可以参考文章:https://blog.51cto.com/xwandrew/2156700

MaxMultipartMemory int64


这个参数会直接传给http.Request的ParseMultipartForm方法。
这个方法用于解析request中multipart/form-data。
这个方法底层通过调用http.Request.multipartReader.ReadForm来解析。
如果文件大小超过maxMemory,则使用临时文件来存储multipart/form中剩余的文件数据。
而非文件部分会存储在不超过10MB的内存中。
可以参考:https://blog.51cto.com/xwandrew/2156700

RemoveExtraSlash bool

。开启后会使用301或者307将包含"//"的path进行路由重定向到正确的地址。
(说实话不太明白什么地方会用到"//"的地址)
可以参考gin项目的issue:
https://github.com/gin-gonic/gin/issues/1644
https://github.com/gin-gonic/gin/pull/1817

delims render.Delims

Delims用于定义一组HTML模板的左右分隔符。
就是类似于"{{ blablabla }}"的模板语言。
使用了http/template包。

secureJsonPrefix string
HTMLRender render.HTMLRender

type HTMLRender interface {
	// Instance returns an HTML instance.
	Instance(string, interface{}) Render
}
interface类型,在render/html.go中定义。结构体HTMLDebug和HTMLProduction实现了这个接口。
HTMLDebug与HTMLProduction实现方法基本一致,都返回了一个HTML实现的Render。
不同的是HTMLDebug中返回的HTML对Template成员多了一次处理。
详见html.go
其中,Render定义如下。此接口有JSON, XML,HTML等一系列的实现。
type Render interface {
	// Render writes data with custom ContentType.
	Render(http.ResponseWriter) error
	// WriteContentType writes custom ContentType.
	WriteContentType(w http.ResponseWriter)
}

FuncMap template.FuncMap

type FuncMap map[string]interface{}
map类型,定义于go的package html/template,与go本身的package text/template一样。
是名称到函数的映射,要求函数必须有一或者两个返回值,有两个返回值时,第二个返回值必须是error类型。
此函数在package html/template中用于注册模板函数。
参考:https://www.cnblogs.com/gzww/articles/11014804.html

allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain

noRoute和noMethod用于定义出现没有路由或者没有对应的方法(POST,GET...)时对应的处理程序序列(HandlersChanin)。
noRoute应用之一就是定义返回的404页面。而带前缀“all”的成员使用了routergroup.go中的
		func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain
方法,将RouterGroup中的Handlers成员和新的处理程序绑定,共同形成新的HandlersChan(处理程序序列)。
也就是说allNoRoute这条处理程序序列就是“RouterGroup的成员Handlers” + “Engine的成员noRoute”

pool sync.Pool

与golang的GC机制相关。定义了一个临时对象池。
可以参考:https://www.jianshu.com/p/2bd41a8f2254

trees methodTrees

该类型定义于同属于package gin的tree.go文件中,具体如下
type methodTree struct {
	method string
	root   *node
}
type methodTrees []methodTree
node的定义如下
type node struct {
	path      string
	indices   string
	children  []*node
	handlers  HandlersChain
	priority  uint32
	nType     nodeType
	maxParams uint8
	wildChild bool
	fullPath  string
}
实际上methodTree结构体定义了一个方法与路由的关系。
可以参考:https://segmentfault.com/a/1190000016655709

Engine结构体方法概述

func New() *Engine

1、使用默认设置创建一个空白的Engine实例。这个实例不包含任何middleware。
2、将RouterGroup与Engine实例绑定
3、创建一个sync.Pool,并使用engine.allocateContext对sync.Pool.New进行设置。
4、返回engine实例
sync.Pool是一个对象池,与golang的GC机制相关。具体可以参考:
https://www.cnblogs.com/linyihai/p/10295411.html
https://www.cnblogs.com/52php/p/7076484.html

func Default() *Engine

1、通过Engine的New方法创建一个Engine实例
2、使用Engine的Use方法绑定middleware(Logger方法和Recovery方法)。
实际上是将这两个方法添加到了Engine.RouterGroup.Handlers中。
Logger方法实现输出日志。而Recovery方法实现了如果request使得engine出现了panic,
那么Recovery会重启程序。实际上Recovery调用的是内建函数recover()

func (engine *Engine) Delims(left, right string) *Engine

设置engine的delims字段

func (engine *Engine) SecureJsonPrefix(prefix string) *Engine

设置engine的secureJsonPrefix字段

func (engine *Engine) LoadHTMLGlob(pattern string)
func (engine *Engine) LoadHTMLFiles(files …string)

以上两个函数设置了HTML模板。区别在于一个调用了ParseGlob而另一个调用的是ParseFiles。这也就是说,前者定义模板时给的参数是某目录下的所有文件,而后者使用的是slice接受数个指定的文件。

func (engine *Engine) SetHTMLTemplate(templ *template.Template)

使用Engine中的FuncMap成员定义模板渲染器。
用法可以参考:https://blog.csdn.net/u013210620/article/details/82797810

func (engine *Engine) SetFuncMap(funcMap template.FuncMap)

用以自定义模板函数

func (engine *Engine) NoRoute(handlers …HandlerFunc)

自定义没有对应路由的处理函数

func (engine *Engine) NoMethod(handlers …HandlerFunc)

自定义没有对应方法的处理函数

func (engine *Engine) Use(middleware …HandlerFunc) IRoutes

添加一个包含HandlersChain的全局的middleware,这个middleware实际上就是HandlerChain类型。
这里的Use方法实际上调用的是同属于package gin的routergroup.go中的Use方法,Use方法会直接通过append方法添加到RouterGroup结构体的Handlers成员中。
然后gin.go中的Use方法再调用rebuild404Handlers和rebuild405Handlers将新增加的HandlersChain添加到engine.allNoRoute和engine.allNoMethod中。

func (engine *Engine) Routes() (routes RoutesInfo)

用以获取已注册路由的信息,包括该路由的方法,地址和处理函数的名称。实际上该方法操作的是Engine结构体中的trees methodTrees成员。
返回值类型为RoutesInfo,定义于gin.go文件开始部分:
type RouteInfo struct {
	Method      string
	Path        string
	Handler     string
	HandlerFunc HandlerFunc
}
type RoutesInfo []RouteInfo
实际上,这个函数使用递归的方式获取了关于这个Engine结构体实例下的所有的(method-path-handler)对应的信息。

func (engine *Engine) Run(addr …string) (err error)

默认的Run方法调用同属于pacakge gin的utils.go文件中的func resolveAddress(addr []string) string方法解析地址和端口。
注意:除非发生错误,否则此方法将无限期阻塞调用goroutine(此话出于Run方法注释)。
Run方法调用会http.ListenAndServe方法,该方法进行网络IO时会导致线程阻塞。可以使用关键字go启动server。
现象参考:
(https://studygolang.com/topics/2590/comment/7238以及http://blog.yoqi.me/lyq/16889.html)
理解参考:
https://www.e-learn.cn/content/qita/2691775

func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error)

开启并接收HTTPS请求。调用的是func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error方法

func (engine *Engine) RunUnix(file string) (err error)

开启服务器时使用指定的unix.socket。调用的是net包下的Listen(network, address string) (Listener, error)。
实际上,RunTLS和Run所调用的方法底层调用的也是net.Listen方法。
只不过不同的是,之前的两个方法在调用时传入的是ln, err := net.Listen("tcp", addr)。
而RunUnix方法直接指定了socketfile。

func (engine *Engine) RunFd(fd int) (err error)

更进一步,指定自定义文件。

func (engine *Engine) RunListener(listener net.Listener) (err error)

为以上几种run方法提供创建httpserver的方法。该方法直接调用http.Serve创建http服务器。
除此以外,还提供了错误打印的功能。

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request)

为Engine结构体实现golang 内建package http中的Handler接口。
以便于可以使用package http中的func ListenAndServe(addr string, handler Handler) error方法启动服务器。

func (engine *Engine) HandleContext(c *Context)

使用技巧

鼠标悬停在语句上可获取解释

var _ IRouter = &Engine{}
详细说明

参考文章

参考文章1

你可能感兴趣的:(源码阅读,gin)