路由实现
1.不用框架
不用框架的路由实现
go
package main
import (
"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.路由实现
go
package main
import (
"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)
}
}
测试代码
go
package main
import (
"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.实现分组路由
go
package main
import (
"log"
"net/http"
)
type HandleFunc func(w http.ResponseWriter, r *http.Request)
// 定义路由分组结构
type routerGroup struct {
name string
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)
}
测试代码
go
package main
import (
"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.支持不同的请求方式
go
package main
import (
"fmt"
"log"
"net/http"
)
type HandleFunc func(w http.ResponseWriter, r *http.Request)
// 定义路由分组结构
type routerGroup struct {
name string
handleFuncMap 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]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)
}
}
测试代码
go
package main
import (
"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.支持同一个路径不同请求方式
go
package main
import "net/http"
type Context struct {
W http.ResponseWriter
R *http.Request
}
go
package main
import (
"fmt"
"log"
"net/http"
)
const ANY = "ANY"
type HandleFunc func(ctx *Context)
// 定义路由分组结构
type routerGroup struct {
name string
handleFuncMap map[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)
}
}
测试代码
go
package main
import (
"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
这些。
实现代码
go
package msgo
import "strings"
type treeNode struct {
name string
children []*treeNode
routerName string
isEnd bool
}
//put path: /user/get/:id
func (t *treeNode) Put(path string) {
root := t
strs := strings.Split(path, "/")
for index, name := range strs {
if index == 0 {
continue
}
children := t.children
isMatch := false
for _, node := range children {
if node.name == name {
isMatch = true
t = node
break
}
}
if !isMatch {
isEnd := false
if index == len(strs)-1 {
isEnd = true
}
node := &treeNode{name: name, children: make([]*treeNode, 0), isEnd: isEnd}
children = append(children, node)
t.children = children
t = node
}
}
t = root
}
//get path:/user/get/11
// hello
func (t *treeNode) Get(path string) *treeNode {
strs := strings.Split(path, "/")
routerName := ""
for index, name := range strs {
if index == 0 {
continue
}
children := t.children
isMatch := false
for _, node := range children {
if node.name == name || node.name == "*" ||
strings.Contains(node.name, ":") {
isMatch = true
routerName += "/" + node.name
node.routerName = routerName
t = node
//到达最尾部结点
if index == len(strs)-1 {
return node
}
break
}
}
if !isMatch {
for _, node := range children {
// /user/**
// /user/get/userinfo
// /user/aa/bb
if node.name == "**" {
routerName += "/" + node.name
node.routerName = routerName
return node
}
}
}
}
return nil
}
测试代码
go
package msgo
import (
"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适配前缀树
实现代码
go
package msgo
import (
"fmt"
"log"
"net/http"
)
const ANY = "ANY"
type HandleFunc func(ctx *Context)
// 定义路由分组结构
type routerGroup struct {
name string
handleFuncMap map[string]map[string]HandleFunc
handlerMethodMap map[string][]string
treeNode *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] = handleFunc
r.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.Method
for _, group := range e.routerGroups {
routerName := SubStringLast(r.RequestURI, "/"+group.name)
// get/1
node := 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)
}
}
测试代码
go
package main
import (
"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)
}
}
测试代码
```go
package main
import (
"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()
}