注意这里的path使我们用来构造路由表要存入的目标path
// router 中 addRoute 方法,在 handlers map[string]HandlerFunc 中存入路由对应处理方法 //存入形式为例如:{ "GET-/index" : 定义的处理方法 } func (r *router) addRoute(method string, path string, handler HandlerFunc) { parts := parsePath(path) log.Printf("Route %4s - %s",method,path) key := method + "-" + path _, ok := r.roots[method] //roots中不存在对应的方法入口则注册相应方法入口 if !ok { r.roots[method] = &node{} } //调用路由表插入方法,在该数据结构中插入该路由 r.roots[method].insert(path, parts, 0) //把method-path作为key,以及handler方法作为value注入数据结构 r.handlers[key] = handler }4、做一个getRoute方法,进入到对应路由树,找到我们的路由,通过哈希表存入处理动态路由拿到param和找到的*node一起返回。
注意代码中的n.path是我们注册在路由表中的路由,path是外部传入的!
func (r *router) getRoute(method string, path string) (*node, map[string]string) { searchParts := parsePath(path) params := make(map[string]string) root, ok := r.roots[method] if !ok { return nil, nil } n := root.search(searchParts, 0)//传入全部路径的字符串数组,寻找到最后对应节点 if n != nil { parts := parsePath(n.path) //n.path包含了完整的路由 for i, part := range parts {//遍历这一条路径 //拿到:的参数,存入params,方法中的part作为key,外面传入的path中的数据作为value存入 if part[0] == ':' { params[part[1:]] = searchParts[i] } //拿到*,此时路由表中的存入的part作为key,外面传入的path中的数据作为value传入params,之后也再没有了 if part[0] == '*' && len(part) > 1{ params[part[1:]] = strings.Join(searchParts[i:],"http://www.likecs.com/") break } } return n, params } return nil, nil }5.同时我们的hanle方法和上一篇文章不同的是,不是直接拿外部传入的path直接在 handlers map[string]HandlerFunc找对应的方法,因为我们外部传入的path是动态的。我们是先通过getRoute方法拿到参数和对应的找到存储节点,用这个节点中存储的path(它是静态的,是我们之前注入的),再在 handlers map[string]HandlerFunc找到对应的方法。
//根据context中存储的 c.Method 和 c.Path 拿到对应的处理方法,进行执行,如果拿到的路由没有注册,则返回404 func (r *router) handle(c *Context) { //获取匹配到的节点,同时也拿到两类动态路由中参数 n, params := r.getRoute(c.Method, c.Path) if n != nil { c.Params = params //拿目的节点中的path做key来找handlers key := c.Method + "-" + n.path r.handlers[key](c) }else { c.String(http.StatusNotFound,"404 NOT FOUND") } } 三、Context变更1、修改Context结构体,构造Params来存放处理动态路由拿到的参数
// Context 结构体,内部封装了 http.ResponseWriter, *http.Request type Context struct { Writer http.ResponseWriter Req *http.Request //请求的信息,包括路由和方法 Path string Method string Params map[string]string /*用于存储外面拿到的参数 ":xxx" or "*xxx" */ //响应的状态码 StatusCode int }2、设计Param方法,拿到处理动态路由的获取参数
// Param 是c的Param的value的获取方法 func (c *Context) Param(key string) string { value, _ := c.Params[key] return value }随便做个测试:
/* @Time : 2021/8/16 下午4:01 @Author : mrxuexi @File : main @Software: GoLand */ package main import ( "Ez" "net/http" ) func main() { r := Ez.New() r.POST("/hello/:id/*filepath", func(c *Ez.Context) { c.JSON(http.StatusOK,Ez.H{ "name" : c.PostForm("name"), "age" : c.PostForm("age"), "id" : c.Param("id"), "filepath" : c.Param("filepath"), }) }) r.Run(":9090") }成功!
参考:
[1]: https://github.com/geektutu/7days-golang/tree/master/gee-web ""gee""
[2]: https://github.com/gin-gonic/gin ""gin""