Gin 源码阅读 Engine 引擎与路由组

168 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

Engine 引擎对象初始化

我们一般会有两种形式的对象初始化,一个是 gin.New() 另一个是 gin.Default()

  • gin/gin.go 209行 Default()其实就是New()之后新加了两个中间件而已
func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

我们来重点看一下New()的实现。

func New() *Engine {
	debugPrintWARNINGNew()
	engine := &Engine{ // 初始化Engine对象
		RouterGroup: RouterGroup{ // 初始化路由组对象
			Handlers: nil,
			basePath: "/",
			root:     true, // 设置该路由器组为根节点
		},
		
		FuncMap:                template.FuncMap{},
		RedirectTrailingSlash:  true,
		// 为true,如果只有/hello的路由存在,会将请求/hello/ 请求重定向到/hello , GET 响应到301, 其他响应到307。
		RedirectFixedPath:      false,
		// 如果找不到路由,尝试修复请求路径。例如 /HELLO 和 /../../HEllo 可以重定向到/hello。
		HandleMethodNotAllowed: false,
		// 是否对不允许的方法,做对应的响应;开启后,入股用POST方法请求[GET /user] ,请求将由[GET /user]处理
		
		ForwardedByClientIP:    true,
		RemoteIPHeaders:        []string{"X-Forwarded-For", "X-Real-IP"},
		TrustedPlatform:        defaultPlatform,
		UseRawPath:             false,
		RemoveExtraSlash:       false,
		UnescapePathValues:     true,
		MaxMultipartMemory:     defaultMultipartMemory,
		// 提供给http.Request的ParseMultipartForm方法调用的“maxMerory”参数的值。默认是32MB
		trees:                  make(methodTrees, 0, 9),
		// 创建容量为9的redix树切片,对应9种请求方法。
		delims:                 render.Delims{Left: "{{", Right: "}}"},
		secureJSONPrefix:       "while(1);",
		trustedProxies:         []string{"0.0.0.0/0", "::/0"},
		trustedCIDRs:           defaultTrustedCIDRs,
	}
	engine.RouterGroup.engine = engine
	engine.pool.New = func() any {
	// 设置 sync.Pool 新建上下文对象函数
		return engine.allocateContext()
	}
	return engine
}

Router路由

1 Group路由器组

r.Group() 可以帮助我们更快归纳某种请求。

  • gin/routergroup.go 文件

创建路由组,仅是返回路由组对象,路由组的本质就是一个模板,使用路由组添加路由,省去用户填写相同路径前缀和中间件的步骤

func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
	// 返回一个路由组对象
	return &RouterGroup{
	// 新路由器组继承父路由器组的所有处理器
		Handlers: group.combineHandlers(handlers),
		basePath: group.calculateAbsolutePath(relativePath),
		// 将绝对路径计算成相对路径
		engine:   group.engine,
	}
}
  • combineHandlers()方法
type HandlersChain []HandlerFunc

func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
	finalSize := len(group.Handlers) + len(handlers)
	// 将原来的处理器长度加上放入当前需要追加的处理器长度
	assert1(finalSize < int(abortIndex), "too many handlers")
	// 如果超过了63中间件,这个路由是无法进行一个添加的,太多中间件要处理了。
	mergedHandlers := make(HandlersChain, finalSize)
	copy(mergedHandlers, group.Handlers)
	// 把旧的中间件都拷贝到新创建的切片中
	copy(mergedHandlers[len(group.Handlers):], handlers)
	// 把新的也追加到这个新的创建的切片中
	return mergedHandlers
}
func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
	return joinPaths(group.basePath, relativePath)
	// 根据绝对路径进行拼接成相对路径
}

2 GET 路由

路由是怎么进行注册的呢?我们通过GET方法来了解一下是怎么处理的

  • gin/routergroup.go
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodGet, relativePath, handlers)
}

然后我们来看看这个handle()方法

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
	absolutePath := group.calculateAbsolutePath(relativePath)
	// 根据相对路径,计算绝对路径
	handlers = group.combineHandlers(handlers)
	// 合并处理器(实际上就是将handlers追加到原有的处理器组切片中,作为该路径的处理链)
	group.engine.addRoute(httpMethod, absolutePath, handlers)
	// 添加路由,涉及radix树添加节点方法。
	return group.returnObj()
}