文章目录
GEE 基本框架篇 – 路由篇之router
- 详细可查看https://github.com/OddSpilit/gee/router.go 源码
入口文件
这块没啥好说的,就是调用了路由的组件的入口,我们重点来分析路由组件的代码结构
package main
import (
"gee/gee"
)
func main() {
gee.InitRouter()
}
路由结构体
- 我们先来看看Engine的结构体以及调用的方法
- 有写过go的朋友看到这一个struct还是很清晰的,前面router,RouterGroup以及groups是我们自己定义的结构,下面两个template成员是go自带的包,只是为了渲染html方便而已。这篇文章会着重讲解一下router结构体以及方法的实现🤡。
- 方法分析:
- New:创建一个实例。
- addRouter:新建一个路由依赖成员router里面的addRoute方法。
- GET && POST: 基于addRoute创建一个路由。
- ServeHTTP: net包的一个钩子,当请求进来的时候会来到这个方法。
- Run: 启动服务。
- SetFuncMap && LoadHtmlGlob:前端页面渲染操作。
router结构体
备注: roots类型是map[string]*node
; handlers类型是map[string]HandlerFunc
HandleFunc是一个func(c Context)结构,后面讲到Context会细聊,先挖个坑🧑🔧
所依赖的node结构体
- 结构分析
- pattern:最终保存的路由。
- part:当前匹配到的节点。
- children:子节点。
- isWild:是否精确匹配。
- 方法分析
- matchChild跟matchChildren比较简单,就是匹配传进来的part是否与node.part相等或者是isWild等于true,是的话返回该子节点或者是放回多个子节点。
- insert:这个方法还是比较简单的就是递归查找并且插入的过程。
- search: 逻辑跟insert差不多,只是search是通过多个子节点去查找是否有对应的节点信息。最后返回不为nil就代表了路由匹配成功了,只要中间有一个为nil都是失败结果。
- 贴一下代码看看🧑🏼💻
/**
插入操作
*/
func (n *node) insert(pattern string, parts []string, height int) {
if len(parts) == height {
n.pattern = pattern
return
}
part := parts[height]
child := n.matchChild(part)
if child == nil {
child = &node{part: part, isWild: part[0] == ':' || part[0] == '*'}
n.children = append(n.children, child)
}
child.insert(pattern, parts, height+1)
}
/**
搜索
*/
func (n *node) search(parts []string, height int) *node {
if len(parts) == height || strings.HasPrefix(n.part, "*") {
if n.pattern == "" {
return nil
}
return n
}
part := parts[height]
children := n.matchChildren(part)
for _, child := range children {
result := child.search(parts, height+1)
if result != nil {
return result
}
}
return nil
}
- 其实不难发现,node结构只是帮router做了节点的匹配,插入,搜索处理,即他会将一个路由/v1/test/hello拆分成三个节点v1,test,hello的结构做处理。还有一种特殊情况,如果是匹配到了**“*”** || **“:”**这两种结构,就知道匹配结束了。
- 我们现在又可以回到我们的主线任务了,讲解router的方法。
依赖的切割方法
func parsePattern(pattern string) []string {
vs := strings.Split(pattern, "/")
parts := make([]string, 0)
for _, item := range vs {
if item != "" {
parts = append(parts, item)
if item[0] == '*' {
break
}
}
}
return parts
}
- 这块代码仅仅只是帮助router做了路由切割的工作,返回一个字符串切片。
主体方法代码分析
addRoute
func (r *router) addRoute(method, pattern string, handler HandlerFunc) {
parts := parsePattern(pattern) // 切割注册路由
/**
通过method跟路由拼接作为handler的key
然后就是简单的往router.root 插入node
*/
key := method + "-" + pattern
_, ok := r.roots[method]
if !ok {
r.roots[method] = &node{}
}
r.roots[method].insert(pattern, parts, 0)
r.handlers[key] = handler // 只是挂了一个处理方法操作,后面章节补齐
}
getRoute
func (r *router) getRoute(method, pattern string) (*node, map[string]string) {
searParts := parsePattern(pattern) // 切割请求路由
params := make(map[string]string) // 搜集路由参数
root, ok := r.roots[method]
if !ok {
return nil, nil
}
n := root.search(searParts, 0) // 检索路由是否存在,拿到注册路由
if n != nil {
parts := parsePattern(n.pattern) // 切割注册路由,分析是否出现了'*',':',搜集参数
for index, part := range parts {
if part[0] == ':' {
params[part[1:]] = searParts[index]
}
if part[0] == '*' && len(part) > 1 {
params[part[1:]] = strings.Join(searParts[index:], "/")
}
}
return n, params
}
return nil, nil
}
handle
func (r *router) handle(c *Context) {
n, params := r.getRoute(c.Method, c.Path)
if n != nil {
c.Params = params
key := c.Method + "-" + n.pattern
c.handlers = append(c.handlers, r.handlers[key])
} else {
c.handlers = append(c.handlers, func(c *Context) {
c.String(http.StatusNotFound, "404 NOT FOUND: %s\n", c.Path)
})
}
c.Next()
}
- 判断是否存在对应的路由,去执行相应的handle,这里需要注意**c.Next()**方法,会在后面的章节会聊到~👻
总结
- 这章主要通过Engine结构体引出router结构体以及跟它相关的操作、依赖的方法、结构体信息代码等解析。
- 这章也埋下了一些点,比如Engine其他成员的代码详解、HandlerFunc类型操作以及自实现Context包的逻辑,后面都会一一补齐。
- 下一章还是会先讲解一下RouteGroup操作。