【Golang】gin框架创建路由源码分析

文章目录

源码分析

下面是Gin框架中登记GET、POST路由的函数实现。

// POST is a shortcut for router.Handle("POST", path, handlers).
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodPost, relativePath, handlers)
}

// GET is a shortcut for router.Handle("GET", path, handlers).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodGet, relativePath, handlers)
}

使用RouterGroup类型的对象,该函数将调用的方法类型(POST、GET、PUT等)、请求路径以及其他处理函数传入handle。返回值为IRoutes类型,这是一个接口,实现了POST、GET、PUT等方法。
首先我们来看RouterGroup类型对象的生成函数。使用了一个RouterGroup生成了另一个子RouterGroup,生成时绑定了新生成的组在当前组路径上的附加路径。比如,我使用/v1组生成一个/api组,那么我访问该组路由的路径即为/v1/api/xxx。然后,还在当前组的基础上,绑定了一些处理函数(即中间件),并将之前RouterGroup与新的RouterGroup的处理函数拼接在一起。对应使用的函数下文会进行分析。

// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
// For example, all the routes that use a common middleware for authorization could be grouped.
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
	return &RouterGroup{
		Handlers: group.combineHandlers(handlers),
		basePath: group.calculateAbsolutePath(relativePath),
		engine:   group.engine,
	}
}

接着,让我们来看处理各种路由方法的handle函数。

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
	absolutePath := group.calculateAbsolutePath(relativePath)
	handlers = group.combineHandlers(handlers)
	group.engine.addRoute(httpMethod, absolutePath, handlers)
	return group.returnObj()
}

其中第一个函数用于给当前传入的路径加上所在组的路径,比如当前RouterGroup为api,请求路径为/login,那么最后拼接得到的路径即为/api/login。

func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
	return joinPaths(group.basePath, relativePath)
}

// 非源码,为创建路由组的例子
apiGroup := router.Group("/api")

然后,看combineHandlers函数,传入的是一系列处理函数,返回的是HandlerChain,即一个处理函数数组。
这一步是将当前传入的handler函数与该组包含的handler函数结合,形成一个新的handler函数序列。

// HandlersChain defines a HandlerFunc slice.
type HandlersChain []HandlerFunc

func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
	finalSize := len(group.Handlers) + len(handlers)
	assert1(finalSize < int(abortIndex), "too many handlers")
	mergedHandlers := make(HandlersChain, finalSize)
	copy(mergedHandlers, group.Handlers)
	copy(mergedHandlers[len(group.Handlers):], handlers)
	return mergedHandlers
}

接着,handle运行了Engine下的addRoute函数。
参数分别为:
● method:请求的方法(POST、GET、PUT、DELETE等)
● path:路由的完整路径,拼接了所有父group的路径值
● handlers:路由的处理函数,拼接了所有父group的处理函数

func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
	assert1(path[0] == '/', "path must begin with '/'")
	assert1(method != "", "HTTP method can not be empty")
	assert1(len(handlers) > 0, "there must be at least one handler")

	debugPrintRoute(method, path, handlers)

	root := engine.trees.get(method)
	if root == nil {
		root = new(node)
		root.fullPath = "/"
		engine.trees = append(engine.trees, methodTree{method: method, root: root})
	}
	root.addRoute(path, handlers)

	if paramsCount := countParams(path); paramsCount > engine.maxParams {
		engine.maxParams = paramsCount
	}

	if sectionsCount := countSections(path); sectionsCount > engine.maxSections {
		engine.maxSections = sectionsCount
	}
}

首先会使用assert1,判断三个条件是否满足,如不满足,则发出panic。
接着,判断当前引擎中,是否记录了method类型的方法。如果没有,则新开一个如下的结构体,专门用于记录当前引擎下存放的这类型方法。

type methodTree struct {
	method string
	root   *node
}

type node struct {
	path      string
	indices   string
	wildChild bool
	nType     nodeType
	priority  uint32
	children  []*node // child nodes, at most 1 :param style node at the end of the array
	handlers  HandlersChain
	fullPath  string
}

如果检测到当前引擎之前记录过这一类型的方法,则必将存在对应的methodTree,返回对应的node指针。
接着。执行func (n *node) addRoute(path string, handlers HandlersChain) 方法将当前路由的路径和处理函数添加到engine对应的methodTree中。addRoute函数比较复杂,有100多行,这里就不放了。感兴趣的可以前往gin包下的tree.go文件中查找阅读。值得注意的是文件的165行那个walk,相当于c++里的goto,并不是什么关键字,而是一个标签。
最后调用group.returnObj(),如下所示,返回当前的group对象。group.root是一个bool值,只有在当前engine为使用New()方法创建时才为true。也就是除了根路由组,其他子路由组的root值都为false。

func (group *RouterGroup) returnObj() IRoutes {
	if group.root {
		return group.engine
	}
	return group
}

总结

Gin在添加一个路由方法(GET、POST、PUT)时,首先结合路由所在组,获取绝对路径以及需要执行的所有函数。然后将这个路由的相关信息添加到engine并存储入一个node结构体中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值