路由实现

1.不用框架

不用框架的路由实现

package mainimport ( "fmt" "log" "net/http")func main() { http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {fmt.Fprintf(writer, "%s 欢迎来到goweb教程", "lisus.com") }) err := http.ListenAndServe(":8111", nil) if err != nil {log.Fatal(err) }}

2.路由实现

package mainimport ( "log" "net/http")type HandleFunc func(w http.ResponseWriter, r *http.Request)//定义路由结构体type router struct {//定义一个路由处理函数map handleFuncMap map[string]HandleFunc}//定义引擎type Engine struct { router}func (r *router) Add(name string, handleFunc HandleFunc) { r.handleFuncMap[name] = handleFunc}func New() *Engine { return &Engine{router: router{handleFuncMap: make(map[string]HandleFunc)}, }}func (e *Engine) Run() {//循环处理路由 for key, value := range e.handleFuncMap {http.HandleFunc(key, value) } err := http.ListenAndServe(":8111", nil) if err != nil {log.Fatal(err) }}

测试代码

package mainimport ( "fmt" "net/http")func main() { engine := New() engine.Add("/hello", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000") }) engine.Run()}

3.实现分组路由

package mainimport ( "log" "net/http")type HandleFunc func(w http.ResponseWriter, r *http.Request)// 定义路由分组结构type routerGroup struct { namestring handleFuncMap map[string]HandleFunc}type router struct { routerGroups []*routerGroup}// Group 分组方法func (r *router) Group(name string) *routerGroup { routerGroup := &routerGroup{name:name,handleFuncMap: make(map[string]HandleFunc), } r.routerGroups = append(r.routerGroups, routerGroup) return routerGroup}// Add 添加路由func (r *routerGroup) Add(name string, handleFunc HandleFunc) { r.handleFuncMap[name] = handleFunc}type Engine struct { router}func New() *Engine { return &Engine{router: router{}, }}func (e *Engine) Run() { //user key:get value func for _, group := range e.routerGroups {for key, value := range group.handleFuncMap { http.HandleFunc("/"+group.name+key, value)} } err := http.ListenAndServe(":8111", nil) if err != nil {log.Fatal(err) }

测试代码

package mainimport ( "fmt" "net/http")func main() { engine := New() g := engine.Group("user") g.Add("/hello", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000") }) g.Add("/info", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s info功能", "lisus2000") }) engine.Run()}

4.支持不同的请求方式

package mainimport ( "fmt" "log" "net/http")type HandleFunc func(w http.ResponseWriter, r *http.Request)// 定义路由分组结构type routerGroup struct { name string handleFuncMapmap[string]HandleFunc handlerMethodMap map[string][]string}type router struct { routerGroups []*routerGroup}// Group 分组方法func (r *router) Group(name string) *routerGroup { routerGroup := &routerGroup{name: name,handleFuncMap:make(map[string]HandleFunc),handlerMethodMap: make(map[string][]string), } r.routerGroups = append(r.routerGroups, routerGroup) return routerGroup}// Add 添加路由func (r *routerGroup) Add(name string, handleFunc HandleFunc) { r.handleFuncMap[name] = handleFunc}func (r *routerGroup) Any(name string, handleFunc HandleFunc) { r.handleFuncMap[name] = handleFunc r.handlerMethodMap["ANY"] = append(r.handlerMethodMap["ANY"], name)}func (r *routerGroup) Get(name string, handleFunc HandleFunc) { r.handleFuncMap[name] = handleFunc r.handlerMethodMap[http.MethodGet] = append(r.handlerMethodMap[http.MethodGet], name)}func (r *routerGroup) Post(name string, handleFunc HandleFunc) { r.handleFuncMap[name] = handleFunc r.handlerMethodMap[http.MethodPost] = append(r.handlerMethodMap[http.MethodPost], name)}type Engine struct { router}func New() *Engine { return &Engine{router: router{}, }}func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) { method := r.Method for _, group := range e.routerGroups {for name, methodHandle := range group.handleFuncMap { url := "/" + group.name + name if r.RequestURI == url {routers, ok := group.handlerMethodMap["ANY"]if ok { for _, routerName := range routers {if routerName == name { methodHandle(w, r) return} }}//method 进行匹配routers, ok = group.handlerMethodMap[method]if ok { for _, routerName := range routers {if routerName == name { methodHandle(w, r) return} }}w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)return }} } w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "%s not found\n", r.RequestURI)}func (e *Engine) Run() { http.Handle("/", e) err := http.ListenAndServe(":8111", nil) if err != nil {log.Fatal(err) }}

测试代码

package mainimport ( "fmt" "net/http")func main() { engine := New() g := engine.Group("user") g.Get("/hello", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s 欢迎学习GO自研框架", "lisus2000") }) g.Post("/info", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "%s info功能", "lisus2000") }) engine.Run()}

5.支持同一个路径不同请求方式

package mainimport "net/http"type Context struct { W http.ResponseWriter R *http.Request}
package mainimport ( "fmt" "log" "net/http")const ANY = "ANY"type HandleFunc func(ctx *Context)// 定义路由分组结构type routerGroup struct { name string handleFuncMapmap[string]map[string]HandleFunc handlerMethodMap map[string][]string}type router struct { routerGroups []*routerGroup}// Group 分组方法func (r *router) Group(name string) *routerGroup { routerGroup := &routerGroup{name: name,handleFuncMap:make(map[string]map[string]HandleFunc),handlerMethodMap: make(map[string][]string), } r.routerGroups = append(r.routerGroups, routerGroup) return routerGroup}func (r *routerGroup) handle(name string, method string, handleFunc HandleFunc) { _, ok := r.handleFuncMap[name] if !ok {r.handleFuncMap[name] = make(map[string]HandleFunc) } _, ok = r.handleFuncMap[name][method] if ok {panic("有重复的路由") } r.handleFuncMap[name][method] = handleFunc //r.handlerMethodMap[method] = append(r.handlerMethodMap[method], name)}func (r *routerGroup) Any(name string, handleFunc HandleFunc) { r.handle(name, ANY, handleFunc)}func (r *routerGroup) Get(name string, handleFunc HandleFunc) { r.handle(name, http.MethodGet, handleFunc)}func (r *routerGroup) Post(name string, handleFunc HandleFunc) { r.handle(name, http.MethodPost, handleFunc)}type Engine struct { router}func New() *Engine { return &Engine{router: router{}, }}func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) { method := r.Method for _, group := range e.routerGroups {for name, methodHandle := range group.handleFuncMap { url := "/" + group.name + name if r.RequestURI == url {ctx := &Context{ W: w, R: r,}handle, ok := methodHandle[ANY]if ok { handle(ctx) return}handle, ok = methodHandle[method]if ok { handle(ctx) return}//method 进行匹配w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)return }} } w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "%s not found\n", r.RequestURI)}func (e *Engine) Run() { http.Handle("/", e) err := http.ListenAndServe(":8111", nil) if err != nil {log.Fatal(err) }}

测试代码

package mainimport ( "fmt")func main() { engine := New() g := engine.Group("user") g.Get("/hello", func(ctx *Context) {fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000") }) g.Post("/hello", func(ctx *Context) {fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000") }) g.Post("/info", func(ctx *Context) {fmt.Fprintf(ctx.W, "%s info功能", "lisus2000") }) engine.Run()}

6.前缀树

在前面实现的时候,我们的路径匹配实现的很简陋,不能实现更为复杂的需求,比如/user/get/:id 这种带有参数的,这种带有参数的路径,我们称之为动态路由

除了带有参数的,一般情况下,我们可能还希望支持通配符**,比如/static/**, 可以匹配/static/vue.js或者/static/css/index.css这些。

实现代码

package msgoimport "strings"type treeNode struct {name stringchildren []*treeNoderouterName stringisEndbool}//put path: /user/get/:idfunc (t *treeNode) Put(path string) {root := tstrs := strings.Split(path, "/")for index, name := range strs {if index == 0 {continue}children := t.childrenisMatch := falsefor _, node := range children {if node.name == name {isMatch = truet = nodebreak}}if !isMatch {isEnd := falseif index == len(strs)-1 {isEnd = true}node := &treeNode{name: name, children: make([]*treeNode, 0), isEnd: isEnd}children = append(children, node)t.children = childrent = node}}t = root}//get path:/user/get/11// hellofunc (t *treeNode) Get(path string) *treeNode {strs := strings.Split(path, "/")routerName := ""for index, name := range strs {if index == 0 {continue}children := t.childrenisMatch := falsefor _, node := range children {if node.name == name || node.name == "*" ||strings.Contains(node.name, ":") {isMatch = truerouterName += "/" + node.namenode.routerName = routerNamet = node//到达最尾部结点if index == len(strs)-1 {return node}break}}if !isMatch {for _, node := range children {// /user/**// /user/get/userinfo// /user/aa/bbif node.name == "**" {routerName += "/" + node.namenode.routerName = routerNamereturn node}}}}return nil}

测试代码

package msgoimport ( "fmt" "testing")func TestTreeNode(t *testing.T) { root := &treeNode{name: "/",children: make([]*treeNode, 0), } root.Put("/user/get/:id") root.Put("/user/create/hello") root.Put("/user/create/aaa") root.Put("/order/get/aaa") node := root.Get("/user/get/1") fmt.Println(node) node = root.Get("/user/create/hello") fmt.Println(node) node = root.Get("/user/create/aaa") fmt.Println(node) node = root.Get("/order/get/aaa") fmt.Println(node)}

6.1适配前缀树

实现代码

package msgoimport ("fmt""log""net/http")const ANY = "ANY"type HandleFunc func(ctx *Context)// 定义路由分组结构type routerGroup struct {name stringhandleFuncMapmap[string]map[string]HandleFunchandlerMethodMap map[string][]stringtreeNode *treeNode}type router struct {routerGroups []*routerGroup}// Group 分组方法func (r *router) Group(name string) *routerGroup {routerGroup := &routerGroup{name: name,handleFuncMap:make(map[string]map[string]HandleFunc),handlerMethodMap: make(map[string][]string),treeNode: &treeNode{name: "/", children: make([]*treeNode, 0)},}r.routerGroups = append(r.routerGroups, routerGroup)return routerGroup}func (r *routerGroup) handle(name string, method string, handleFunc HandleFunc) {_, ok := r.handleFuncMap[name]if !ok {r.handleFuncMap[name] = make(map[string]HandleFunc)}_, ok = r.handleFuncMap[name][method]if ok {panic("有重复的路由")}r.handleFuncMap[name][method] = handleFuncr.treeNode.Put(name)}func (r *routerGroup) Any(name string, handleFunc HandleFunc) {r.handle(name, ANY, handleFunc)}func (r *routerGroup) Get(name string, handleFunc HandleFunc) {r.handle(name, http.MethodGet, handleFunc)}func (r *routerGroup) Post(name string, handleFunc HandleFunc) {r.handle(name, http.MethodPost, handleFunc)}func (r *routerGroup) Delete(name string, handlerFunc HandleFunc) {r.handle(name, http.MethodDelete, handlerFunc)}func (r *routerGroup) Put(name string, handlerFunc HandleFunc) {r.handle(name, http.MethodPut, handlerFunc)}func (r *routerGroup) Patch(name string, handlerFunc HandleFunc) {r.handle(name, http.MethodPatch, handlerFunc)}func (r *routerGroup) Options(name string, handlerFunc HandleFunc) {r.handle(name, http.MethodOptions, handlerFunc)}func (r *routerGroup) Head(name string, handlerFunc HandleFunc) {r.handle(name, http.MethodHead, handlerFunc)}type Engine struct {router}func New() *Engine {return &Engine{router: router{},}}func (e *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {method := r.Methodfor _, group := range e.routerGroups {routerName := SubStringLast(r.RequestURI, "/"+group.name)// get/1node := group.treeNode.Get(routerName)if node != nil && node.isEnd {//路由匹配上了ctx := &Context{W: w,R: r,}handle, ok := group.handleFuncMap[node.routerName][ANY]if ok {handle(ctx)return}handle, ok = group.handleFuncMap[node.routerName][method]if ok {handle(ctx)return}//method 进行匹配w.WriteHeader(http.StatusMethodNotAllowed)fmt.Fprintf(w, "%s %s not allow\n", r.RequestURI, method)return}}w.WriteHeader(http.StatusNotFound)fmt.Fprintf(w, "%s not found\n", r.RequestURI)}func (e *Engine) Run() {http.Handle("/", e)err := http.ListenAndServe(":8111", nil)if err != nil {log.Fatal(err)}}

测试代码

package mainimport ("fmt""msgo")func main() {engine := msgo.New()g := engine.Group("user")//g.Get("/hello", func(ctx *msgo.Context) {//fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")//})g.Get("/hello/get", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")})g.Post("/hello", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")})g.Post("/info", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")})g.Get("/get/:id", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s get user 路径变量的值", "lisus2000")})engine.Run()}

il)
if err != nil {
log.Fatal(err)
}
}

测试代码```gopackage mainimport ("fmt""msgo")func main() {engine := msgo.New()g := engine.Group("user")//g.Get("/hello", func(ctx *msgo.Context) {//fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")//})g.Get("/hello/get", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Get欢迎学习GO自研框架", "lisus2000")})g.Post("/hello", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s Post欢迎学习GO自研框架", "lisus2000")})g.Post("/info", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s info功能", "lisus2000")})g.Get("/get/:id", func(ctx *msgo.Context) {fmt.Fprintf(ctx.W, "%s get user 路径变量的值", "lisus2000")})engine.Run()}