GEE 基本框架篇 -- 路由篇之router

GEE 基本框架篇 – 路由篇之router

  • 详细可查看https://github.com/OddSpilit/gee/router.go 源码

入口文件

这块没啥好说的,就是调用了路由的组件的入口,我们重点来分析路由组件的代码结构

package main

import (
	"gee/gee"
)

func main() {
	gee.InitRouter()
}

路由结构体

  • 我们先来看看Engine的结构体以及调用的方法
Engine
router *router
*RouterGroup
groups []*RouterGroup
htmlTemplates *template.Template
funcMap template.FuncMap
New
addRouter
GET
POST
ServeHTTP
Run
SetFuncMap
LoadHtmlGlob
  • 有写过go的朋友看到这一个struct还是很清晰的,前面routerRouterGroup以及groups是我们自己定义的结构,下面两个template成员是go自带的包,只是为了渲染html方便而已。这篇文章会着重讲解一下router结构体以及方法的实现🤡。
  • 方法分析:
    • New:创建一个实例。
    • addRouter:新建一个路由依赖成员router里面的addRoute方法。
    • GET && POST: 基于addRoute创建一个路由。
    • ServeHTTPnet包的一个钩子,当请求进来的时候会来到这个方法。
    • Run: 启动服务。
    • SetFuncMap && LoadHtmlGlob:前端页面渲染操作。

router结构体

router
roots map[string]*node
handlers map[string]HandlerFunc
newRouter
getRoute
addRoute
handle

备注: roots类型是map[string]*node; handlers类型是map[string]HandlerFunc

HandleFunc是一个func(c Context)结构,后面讲到Context会细聊,先挖个坑🧑‍🔧

所依赖的node结构体
node
pattern string
part string
children []*node
isWild bool
matchChild
matchChildren
insert
search
  • 结构分析
    • pattern:最终保存的路由。
    • part:当前匹配到的节点。
    • children:子节点。
    • isWild:是否精确匹配。
  • 方法分析
    • matchChildmatchChildren比较简单,就是匹配传进来的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拆分成三个节点v1testhello的结构做处理。还有一种特殊情况,如果是匹配到了**“*”** || **“:”**这两种结构,就知道匹配结束了。
  • 我们现在又可以回到我们的主线任务了,讲解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操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值