文章目录 不用框架实现web接口 实现简单的路由 实现分组路由 支持不同的请求方式 支持同一个路径的不同请求方式 前缀树 应用前缀树 完善路由代码
不用框架实现web接口
package mainimport ( "fmt" "log" "net/http"
) func main ( ) { fmt. Println ( "Hello World!" ) http. HandleFunc ( "/hello" , func ( w http. ResponseWriter, r * http. Request) { fmt. Fprintf ( w, "Hello Go!" ) } ) err := http. ListenAndServe ( "8111" , nil ) if err != nil { log. Fatal ( err) }
}
http.HandleFunc
注册一个路由 /hello
并绑定处理函数。当用户访问 http://localhost:8111/hello
时,执行匿名函数: w http.ResponseWriter
:用于向客户端发送 HTTP 响应。r *http.Request
:包含客户端发来的请求信息(如 URL、Headers 等)。 fmt.Fprintf(w, "Hello Go!")
: 向 HTTP 响应写入 "Hello Go!"
(相当于 w.Write([]byte("Hello Go!"))
)。 默认状态码是 200 OK
。 http.ListenAndServe(":8111", nil)
: 启动 HTTP 服务器,监听 8111
端口(: 表示监听所有网络接口)。 nil
表示使用默认的 DefaultServeMux
路由器(即之前用 http.HandleFunc
注册的路由)。 if err != nil { log.Fatal(err) }
:如果服务器启动失败(如端口被占用),打印错误并终止程序。fmt.Fprintf
是 Go 语言 fmt
包提供的一个格式化输出函数,用于将格式化后的字符串写入指定的 io.Writer
接口(如文件、HTTP 响应、标准输出等)。它的作用类似于 fmt.Printf
,但不是输出到终端,而是写入到任意实现了 io.Writer
的对象。
func Fprintf ( w io. Writer, format string , a ... interface { } ) ( n int , err error )
w io.Writer
:目标写入器(如 http.ResponseWriter
、文件、缓冲区等)。format string
:格式化字符串(包含占位符,如 %s
, %d
, %v
等)。a ...interface{}
:可变参数,用于填充格式化字符串中的占位符。返回值: n int
:成功写入的字节数。err error
:写入过程中遇到的错误(如写入失败)。
实现简单的路由
package zjgoimport ( "log" "net/http"
) type Engine struct {
} func New ( ) * Engine { return & Engine{ }
} func ( e * Engine) Run ( ) { err := http. ListenAndServe ( "8111" , nil ) if err != nil { log. Fatal ( err) }
}
经过封装之后,原来的 main
函数可以简洁为如下:
package mainimport ( "fmt" "github.com/ErizJ/ZJGo/zjgo"
) func main ( ) { fmt. Println ( "Hello World!" ) engine := zjgo. New ( ) engine. Run ( )
}
注意这里服务启动后会 404 Not Found
,因为我们没有实现对应的响应函数 Handler
。
package zjgoimport ( "log" "net/http"
)
type HandleFunc func ( w http. ResponseWriter, r * http. Request)
type router struct { handleFuncMap map [ string ] HandleFunc
}
func ( r * router) Add ( name string , handleFunc HandleFunc) { r. handleFuncMap[ name] = handleFunc
}
type Engine struct { router
}
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 语言中,当你将一个类型(如 router
)嵌入到另一个结构体(如 Engine
)中时,这被称为类型嵌入(Embedded Type
)。这是一种组合的方式,它允许 Engine
直接访问 router
的字段和方法,而不需要显式地通过一个字段名来访问。
package mainimport ( "fmt" "net/http" "github.com/ErizJ/ZJGo/zjgo"
) func main ( ) { fmt. Println ( "Hello World!" ) engine := zjgo. New ( ) engine. Add ( "/hello" , func ( w http. ResponseWriter, r * http. Request) { fmt. Fprintf ( w, "Hello Go!" ) } ) engine. Run ( )
}
这样我们就实现了一个简单的路由功能,下面进行进一步完善。
实现分组路由
大多数情况下我们希望写的接口归属于某一个模块,这样便于管理以及维护,代码也会更为清晰。 例如:/user/getUser
和 /user/createUser
都同属于 user
模块。
package zjgoimport ( "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
}
func ( r * router) Group ( name string ) * routerGroup { routerGroup := & routerGroup{ name: name, handleFuncMap: make ( map [ string ] HandleFunc) , } r. routerGroups = append ( r. routerGroups, routerGroup) return routerGroup
}
func ( routerGroup * routerGroup) Add ( name string , handleFunc HandleFunc) { routerGroup. handleFuncMap[ name] = handleFunc
}
type Engine struct { router
}
func New ( ) * Engine { return & Engine{ router: router{ } , }
}
func ( e * Engine) Run ( ) { for _ , group := range e. routerGroups { for name, value := range group. handleFuncMap { http. HandleFunc ( "/" + group. name+ name, value) } } err := http. ListenAndServe ( ":3986" , nil ) if err != nil { log. Fatal ( err) }
}
package mainimport ( "fmt" "net/http" "github.com/ErizJ/ZJGo/zjgo"
) func main ( ) { fmt. Println ( "Hello World!" ) engine := zjgo. New ( ) g1 := engine. Group ( "user" ) g1. Add ( "/hello" , func ( w http. ResponseWriter, r * http. Request) { fmt. Fprintf ( w, "Hello Go!——user/hello" ) } ) g1. Add ( "/info" , func ( w http. ResponseWriter, r * http. Request) { fmt. Fprintf ( w, "Hello Go!——user/info" ) } ) g2 := engine. Group ( "order" ) g2. Add ( "/hello" , func ( w http. ResponseWriter, r * http. Request) { fmt. Fprintf ( w, "Hello Go!——order/hello" ) } ) g2. Add ( "/info" , func ( w http. ResponseWriter, r * http. Request) { fmt. Fprintf ( w, "Hello Go!——order/info" ) } ) fmt. Println ( "Starting..." ) engine. Run ( ) }
支持不同的请求方式
net / http
下的路由,只要路径匹配,就可以进入处理方法。但是在我们实际应用之中,比如我们使用 Restful 风格的接口在同一路径下,会使用 GET
、POST
、DELETE
、PUT
来代替增删改查,所以我们要对不同的请求方式做相应的支持。
package zjgoimport ( "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
}
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
}
func ( routerGroup * routerGroup) Any ( name string , handleFunc HandleFunc) { routerGroup. handleFuncMap[ name] = handleFuncrouterGroup. handlerMethodMap[ "ANY" ] = append ( routerGroup. handlerMethodMap[ "ANY" ] , name)
}
func ( routerGroup * routerGroup) Post ( name string , handleFunc HandleFunc) { routerGroup. handleFuncMap[ name] = handleFuncrouterGroup. handlerMethodMap[ http. MethodPost] = append ( routerGroup. handlerMethodMap[ http. MethodPost] , name)
}
func ( routerGroup * routerGroup) Get ( name string , handleFunc HandleFunc) { routerGroup. handleFuncMap[ name] = handleFuncrouterGroup. handlerMethodMap[ http. MethodGet] = append ( routerGroup. handlerMethodMap[ http. MethodGet] , name)
}
func ( e * Engine) ServeHTTP ( w http. ResponseWriter, r * http. Request) { if r. Method == http. MethodGet { fmt. Fprintf ( w, "这是一个 GET 请求" ) } else if r. Method == http. MethodPost { fmt. Fprintf ( w, "这是一个 POST 请求" ) } else { fmt. Fprintf ( w, "这是一个其他类型的请求:%s" , r. Method) } for _ , group := range e. routerGroups { for name, methodHandle := range group. handleFuncMap { url := "/" + group. name + nameif r. RequestURI == url { if routers, exist := group. handlerMethodMap[ "ANY" ] ; exist { for _ , routerName := range routers { if routerName == name { methodHandle ( w, r) return } } } if routers, exist := group. handlerMethodMap[ r. Method] ; exist { for _ , routerName := range routers { if routerName == name { methodHandle ( w, r) return } } } w. WriteHeader ( http. StatusMethodNotAllowed) fmt. Fprintf ( w, "%s %s not allowed!!!\n" , r. Method, r. RequestURI) return } } } w. WriteHeader ( http. StatusNotFound) fmt. Fprintf ( w, "%s %s not found!!!\n" , r. Method, r. RequestURI) return
}
type Engine struct { router
}
func New ( ) * Engine { return & Engine{ router: router{ } , }
}
func ( e * Engine) Run ( ) { http. Handle ( "/" , e) err := http. ListenAndServe ( ":3986" , nil ) if err != nil { log. Fatal ( err) }
}
package mainimport ( "fmt" "net/http" "github.com/ErizJ/ZJGo/zjgo"
) func main ( ) { fmt. Println ( "Hello World!" ) engine := zjgo. New ( ) g1 := engine. Group ( "user" ) g1. Get ( "/hello" , func ( w http. ResponseWriter, r * http. Request) { fmt. Fprintf ( w, http. MethodGet+ " Hello Go!——user/hello" ) } ) g1. Post ( "/info" , func ( w http. ResponseWriter, r * http. Request) { fmt. Fprintf ( w, http. MethodPost+ " Hello Go!——user/info" ) } ) g1. Any ( "/any" , func ( w http. ResponseWriter, r * http. Request) { fmt. Fprintf ( w, " Hello Go!——user/any" ) } ) fmt. Println ( "Starting..." ) engine. Run ( )
}
但是目前还是存在一些问题的,目前不支持同一个路由进行 GET
和 POST
,因为在 map 里面会被覆盖。 routerGroup.handleFuncMap[name] = handleFunc
,在 Post
和 Get
方法下都有这段代码,这就会造成方法的覆盖。
支持同一个路径的不同请求方式
标准库 net/http 本身只提供了最基础的路由匹配机制,也就是通过:http.HandleFunc("/path", handler)
,它的匹配机制非常简单:只根据请求路径匹配,不区分请求方法(GET/POST
)。 如果在方法中这样写,是手动在 handler
里面区分请求方法,net/http
本身并不会替你区分。
http. HandleFunc ( "/info" , func ( w http. ResponseWriter, r * http. Request) { if r. Method == http. MethodPost { } else if r. Method == http. MethodGet { } else { http. Error ( w, "Method not allowed" , http. StatusMethodNotAllowed) }
} )
要限制不同请求方法(GET
、POST
分别调用不同的 handler
),就得框架(或者你自己)在 ServeHTTP
里手动实现。 像 Gin、Echo、Fiber 等这些 Web 框架,都是在它们内部封装了: 请求方法和路径的双重匹配机制 路由注册表,支持多种请求方式绑定不同 handler
匹配失败时返回 405 Method Not Allowed
或 404 Not Found
考虑在每个路由组 routerGroup
的 handleFuncMap
上做文章,原先是 map[name]HandleFunc
,现在可以加入 map
中 map
,也即 map[name]map[method]HandleFunc
。
package zjgoimport "net/http"
type Context struct { W http. ResponseWriterR * http. Request
}
package zjgoimport ( "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
}
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 ( routerGroup * routerGroup) handleRequest ( name string , method string , handleFunc HandleFunc) { if _ , exist := routerGroup. handleFuncMap[ name] ; ! exist { routerGroup. handleFuncMap[ name] = make ( map [ string ] HandleFunc) } if _ , exist := routerGroup. handleFuncMap[ name] [ method] ; ! exist { routerGroup. handleFuncMap[ name] [ method] = handleFuncrouterGroup. handlerMethodMap[ method] = append ( routerGroup. handlerMethodMap[ method] , name) } else { panic ( "Under the same route, duplication is not allowed!!!" ) }
}
func ( routerGroup * routerGroup) Any ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, ANY, handleFunc)
}
func ( routerGroup * routerGroup) Post ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, http. MethodPost, handleFunc)
}
func ( routerGroup * routerGroup) Get ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, http. MethodGet, handleFunc)
}
func ( e * Engine) ServeHTTP ( w http. ResponseWriter, r * http. Request) { if r. Method == http. MethodGet { fmt. Fprintf ( w, "这是一个 GET 请求!!!" ) } else if r. Method == http. MethodPost { fmt. Fprintf ( w, "这是一个 POST 请求!!!" ) } else { fmt. Fprintf ( w, "这是一个其他类型的请求:%s" , r. Method) } for _ , group := range e. routerGroups { for name, methodHandleMap := range group. handleFuncMap { url := "/" + group. name + nameif r. RequestURI == url { ctx := & Context{ W: w, R: r} if handle, exist := methodHandleMap[ ANY] ; exist { handle ( ctx) return } if handle, exist := methodHandleMap[ r. Method] ; exist { handle ( ctx) return } w. WriteHeader ( http. StatusMethodNotAllowed) fmt. Fprintf ( w, "%s %s not allowed!!!\n" , r. Method, r. RequestURI) return } } } w. WriteHeader ( http. StatusNotFound) fmt. Fprintf ( w, "%s %s not found!!!\n" , r. Method, r. RequestURI) return
}
type Engine struct { router
}
func New ( ) * Engine { return & Engine{ router: router{ } , }
}
func ( e * Engine) Run ( ) { http. Handle ( "/" , e) err := http. ListenAndServe ( ":3986" , nil ) if err != nil { log. Fatal ( err) }
}
package mainimport ( "fmt" "net/http" "github.com/ErizJ/ZJGo/zjgo"
) func main ( ) { fmt. Println ( "Hello World!" ) engine := zjgo. New ( ) g1 := engine. Group ( "user" ) g1. Get ( "/hello" , func ( ctx * zjgo. Context) { fmt. Fprintf ( ctx. W, http. MethodGet+ " Hello Go!——user/hello" ) } ) g1. Post ( "/info" , func ( ctx * zjgo. Context) { fmt. Fprintf ( ctx. W, http. MethodPost+ " Hello Go!——user/info——POST" ) } ) g1. Get ( "/info" , func ( ctx * zjgo. Context) { fmt. Fprintf ( ctx. W, http. MethodGet+ " Hello Go!——user/info——GET" ) } ) g1. Any ( "/any" , func ( ctx * zjgo. Context) { fmt. Fprintf ( ctx. W, " Hello Go!——user/any" ) } ) fmt. Println ( "Starting..." ) engine. Run ( ) }
前缀树
前面的实现,我们只是实现了静态路由,不能实现更为复杂的需求,比如 /user/get/:id
这种才有参数的。 带有参数的路由路径,成为动态路由。 除了带有参数的,一般情况下我们还希望有更多支持,比如希望支持通配符 **
,比如 /static/**
,可以匹配 /static/vue.js
,/static/css/index.css
这些。
type Trie struct { next [ 26 ] * Trieend bool
} func Constructor ( ) Trie { myTrie := Trie{ } return myTrie
} func ( this * Trie) Insert ( word string ) { if this. Search ( word) { return } node := thisfor _ , ch := range word { if node. next[ ch- 'a' ] == nil { node. next[ ch- 'a' ] = & Trie{ } } node = node. next[ ch- 'a' ] } node. end = true
} func ( this * Trie) Search ( word string ) bool { node := this. search ( word) return node != nil && ( * node) . end
} func ( this * Trie) search ( word string ) * Trie { node := thisfor _ , ch := range word { if node. next[ ch- 'a' ] == nil { return nil } node = node. next[ ch- 'a' ] } return node
} func ( this * Trie) StartsWith ( prefix string ) bool { node := this. search ( prefix) return node != nil
}
package zjgoimport "strings" type TreeNode struct { name string children [ ] * TreeNode
}
func ( t * TreeNode) Put ( path string ) { root := tstrs := strings. Split ( path, "/" ) for index, name := range strs { if index == 0 { continue } isMatch := false for _ , node := range t. children { if node. name == name { isMatch = true t = nodebreak } } if ! isMatch { node := & TreeNode{ name: name, children: make ( [ ] * TreeNode, 0 ) , } t. children = append ( t. children, node) t = node} } t = root
}
func ( t * TreeNode) Get ( path string ) * TreeNode { strs := strings. Split ( path, "/" ) for index, name := range strs { if index == 0 { continue } isMatch := false for _ , node := range t. children { if node. name == name || node. name == "*" || strings. Contains ( node. name, ":" ) { isMatch = true t = nodeif index == len ( strs) - 1 { return node} break } } if ! isMatch { for _ , node := range t. children { if node. name == "**" { return node} } } } return nil
}
package zjgoimport ( "fmt" "testing"
) func TestTreeNode ( t * testing. T) { root := & TreeNode{ name: "/" , children: make ( [ ] * TreeNode, 0 ) } root. Put ( "/user/get/:id" ) root. Put ( "/user/info/hello" ) root. Put ( "/user/create/aaa" ) root. Put ( "/order/get/aaa" ) node := root. Get ( "/user/get/1" ) fmt. Println ( node) node = root. Get ( "/user/info/hello" ) fmt. Println ( node) node = root. Get ( "/user/create/aaa" ) fmt. Println ( node) node = root. Get ( "/order/get/aaa" ) fmt. Println ( node)
} == = RUN TestTreeNode
& { : id [ ] }
& { hello [ ] }
& { aaa [ ] }
& { aaa [ ] }
-- - PASS: TestTreeNode ( 0.00 s)
PASS
ok github. com/ ErizJ/ ZJGo/ zjgo ( cached)
应用前缀树
package zjgoimport "strings"
func SubStringLast ( name string , groupName string ) string { if index := strings. Index ( name, groupName) ; index < 0 { return "" } else { return name[ index+ len ( groupName) : ] }
}
package zjgoimport "strings" type TreeNode struct { name string children [ ] * TreeNode routerName string isEnd bool
}
func ( t * TreeNode) Put ( path string ) { strs := strings. Split ( path, "/" ) for index, name := range strs { if index == 0 { continue } isMatch := false for _ , node := range t. children { if node. name == name { isMatch = true t = nodebreak } } if ! isMatch { node := & TreeNode{ name: name, children: make ( [ ] * TreeNode, 0 ) , isEnd: index == len ( strs) - 1 , } t. children = append ( t. children, node) t = node} } t. isEnd = true
}
func ( t * TreeNode) Get ( path string ) * TreeNode { strs := strings. Split ( path, "/" ) routerName := "" for index, name := range strs { if index == 0 { continue } isMatch := false for _ , node := range t. children { if node. name == name || node. name == "*" || strings. Contains ( node. name, ":" ) { isMatch = true routerName += "/" + node. namenode. routerName = routerNamet = nodeif index == len ( strs) - 1 { return node} break } } if ! isMatch { for _ , node := range t. children { if node. name == "**" { routerName += "/" + node. namenode. routerName = routerNamereturn node} } } } return nil
}
package zjgoimport ( "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
}
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 ( routerGroup * routerGroup) handleRequest ( name string , method string , handleFunc HandleFunc) { if _ , exist := routerGroup. handleFuncMap[ name] ; ! exist { routerGroup. handleFuncMap[ name] = make ( map [ string ] HandleFunc) } if _ , exist := routerGroup. handleFuncMap[ name] [ method] ; ! exist { routerGroup. handleFuncMap[ name] [ method] = handleFuncrouterGroup. handlerMethodMap[ method] = append ( routerGroup. handlerMethodMap[ method] , name) } else { panic ( "Under the same route, duplication is not allowed!!!" ) } routerGroup. TreeNode. Put ( name)
}
func ( routerGroup * routerGroup) Any ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, ANY, handleFunc)
}
func ( routerGroup * routerGroup) Post ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, http. MethodPost, handleFunc)
}
func ( routerGroup * routerGroup) Get ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, http. MethodGet, handleFunc)
}
func ( e * Engine) ServeHTTP ( w http. ResponseWriter, r * http. Request) { if r. Method == http. MethodGet { fmt. Fprintf ( w, "这是一个 GET 请求!!! " ) } else if r. Method == http. MethodPost { fmt. Fprintf ( w, "这是一个 POST 请求!!! " ) } else { fmt. Fprintf ( w, "这是一个其他类型的请求:%s!!! " , r. Method) } for _ , group := range e. routerGroups { routerName := SubStringLast ( r. RequestURI, "/" + group. name) if node := group. TreeNode. Get ( routerName) ; node != nil && node. isEnd { ctx := & Context{ W: w, R: r} if handle, exist := group. handleFuncMap[ node. routerName] [ ANY] ; exist { handle ( ctx) return } if handle, exist := group. handleFuncMap[ node. routerName] [ r. Method] ; exist { handle ( ctx) return } w. WriteHeader ( http. StatusMethodNotAllowed) fmt. Fprintf ( w, "%s %s not allowed!!!\n" , r. Method, r. RequestURI) return } } w. WriteHeader ( http. StatusNotFound) fmt. Fprintf ( w, "%s %s not found!!!\n" , r. Method, r. RequestURI) return
}
type Engine struct { router
}
func New ( ) * Engine { return & Engine{ router: router{ } , }
}
func ( e * Engine) Run ( ) { http. Handle ( "/" , e) err := http. ListenAndServe ( ":3986" , nil ) if err != nil { log. Fatal ( err) }
}
package mainimport ( "fmt" "net/http" "github.com/ErizJ/ZJGo/zjgo"
) func main ( ) { fmt. Println ( "Hello World!" ) engine := zjgo. New ( ) g1 := engine. Group ( "user" ) g1. Get ( "/hello" , func ( ctx * zjgo. Context) { fmt. Fprintf ( ctx. W, http. MethodGet+ " Hello Go!——user/hello" ) } ) g1. Post ( "/info" , func ( ctx * zjgo. Context) { fmt. Fprintf ( ctx. W, http. MethodPost+ " Hello Go!——user/info——POST" ) } ) g1. Get ( "/info" , func ( ctx * zjgo. Context) { fmt. Fprintf ( ctx. W, http. MethodGet+ " Hello Go!——user/info——GET" ) } ) g1. Get ( "/get/:id" , func ( ctx * zjgo. Context) { fmt. Fprintf ( ctx. W, http. MethodGet+ " Hello Go!——user/get/:id——GET" ) } ) g1. Get ( "/isEnd/get" , func ( ctx * zjgo. Context) { fmt. Fprintf ( ctx. W, http. MethodGet+ " Hello Go!——user/isEnd/get——GET" ) } ) g1. Any ( "/any" , func ( ctx * zjgo. Context) { fmt. Fprintf ( ctx. W, " Hello Go!——user/any" ) } ) fmt. Println ( "Starting..." ) engine. Run ( ) }
完善路由代码
package zjgoimport ( "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
}
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 ( routerGroup * routerGroup) handleRequest ( name string , method string , handleFunc HandleFunc) { if _ , exist := routerGroup. handleFuncMap[ name] ; ! exist { routerGroup. handleFuncMap[ name] = make ( map [ string ] HandleFunc) } if _ , exist := routerGroup. handleFuncMap[ name] [ method] ; ! exist { routerGroup. handleFuncMap[ name] [ method] = handleFuncrouterGroup. handlerMethodMap[ method] = append ( routerGroup. handlerMethodMap[ method] , name) } else { panic ( "Under the same route, duplication is not allowed!!!" ) } routerGroup. TreeNode. Put ( name)
}
func ( routerGroup * routerGroup) Any ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, ANY, handleFunc)
}
func ( routerGroup * routerGroup) Post ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, http. MethodPost, handleFunc)
}
func ( routerGroup * routerGroup) Get ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, http. MethodGet, handleFunc)
}
func ( routerGroup * routerGroup) Delete ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, http. MethodDelete, handleFunc)
}
func ( routerGroup * routerGroup) Put ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, http. MethodPut, handleFunc)
}
func ( routerGroup * routerGroup) Patch ( name string , handleFunc HandleFunc) { routerGroup. handleRequest ( name, http. MethodPatch, handleFunc)
}
func ( e * Engine) ServeHTTP ( w http. ResponseWriter, r * http. Request) { if r. Method == http. MethodGet { fmt. Fprintf ( w, "这是一个 GET 请求!!! " ) } else if r. Method == http. MethodPost { fmt. Fprintf ( w, "这是一个 POST 请求!!! " ) } else { fmt. Fprintf ( w, "这是一个其他类型的请求:%s!!! " , r. Method) } for _ , group := range e. routerGroups { routerName := SubStringLast ( r. RequestURI, "/" + group. name) if node := group. TreeNode. Get ( routerName) ; node != nil && node. isEnd { ctx := & Context{ W: w, R: r} if handle, exist := group. handleFuncMap[ node. routerName] [ ANY] ; exist { handle ( ctx) return } if handle, exist := group. handleFuncMap[ node. routerName] [ r. Method] ; exist { handle ( ctx) return } w. WriteHeader ( http. StatusMethodNotAllowed) fmt. Fprintf ( w, "%s %s not allowed!!!\n" , r. Method, r. RequestURI) return } } w. WriteHeader ( http. StatusNotFound) fmt. Fprintf ( w, "%s %s not found!!!\n" , r. Method, r. RequestURI) return
}
type Engine struct { router
}
func New ( ) * Engine { return & Engine{ router: router{ } , }
}
func ( e * Engine) Run ( ) { http. Handle ( "/" , e) err := http. ListenAndServe ( ":3986" , nil ) if err != nil { log. Fatal ( err) }
}