DefaultServeMux
DefaultServeMuxRESTfulAPIquery
httprouter
1 使用
httprouter
package main
import (
"fmt"
"net/http"
"log"
"github.com/julienschmidt/httprouter"
)
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
fmt.Fprint(w, "Welcome!\n")
}
func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}
func main() {
router := httprouter.New()
router.GET("/", Index)
router.GET("/hello/:name", Hello)
log.Fatal(http.ListenAndServe(":8080", router))
}
net/http
router.XXXGETPOST
http.ListenAndServe
至于为什么,我们会在后面的章节详细介绍,现在只需要先了解做法即可。
2 创建
RouterRouter
type Router struct {
//这是前缀树,记录了相应的路由
trees map[string]*node
//记录了参数的最大数目
maxParams uint16
}
Routerrouter.XXX
func (r *Router) GET(path string, handle Handle) {
r.Handle(http.MethodGet, path, handle)
}
func (r *Router) POST(path string, handle Handle) {
r.Handle(http.MethodPost, path, handle)
}
...
在这里还有一长串的方法,他们都是一样的,调用了
r.Handle(http.MethodPost, path, handle)
这个方法。我们再来看看:
func (r *Router) Handle(method, path string, handle Handle) {
...
if r.trees == nil {
r.trees = make(map[string]*node)
}
root := r.trees[method]
if root == nil {
root = new(node)
r.trees[method] = root
r.globalAllowed = r.allowed("*", "")
}
root.addRoute(path, handle)
...
}
tree
mapaddRouteURIHandle
3 前缀树
3.1 定义
又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
简单的来讲,就是要查找什么,只要跟着这棵树的某一条路径找,就可以找得到。
比如在搜索引擎中,你输入了一个编字:
他会有这些联想,也可以理解为是一个前缀树。
再举个例子:
GET
- /wow/awesome
- /test
- /hello/world
- /hello/china
- /hello/chinese
说到这里你应该可以理解了,在构建这棵树的过程中,任何两个节点,只要有了相同的前缀,相同的部分就会被合并成一个节点。
3.2 图解构建
addRoute
假设我们需要插入的三个路由分别为:
- /hello/world
- /hello/china
- /hello/chinese
/hello/world
因为此时树为空,所以可以直接插入:
/hello/china
/hello/world/hello/china/hello/
/hello/world/hello/china/hello/world
/hello/chinese
/hello/chinese/hello/chinese/hello//hello//hello/
indices/hello/indiceswc/hello/chinesechinesecchina
/hello/china/hello/chin
chinaese
3.3 总结构建算法
到这里,构建就已经结束了。我们来总结一下算法。
具体带注释的代码将在本文最末尾给出,如果想要了解的更深可以自行查看。在这里先理解这个过程:
URIHandle
但是到了这里,有同学要问了:怎么这里的路由,不带参数的呀?
/*wildChildtrue
4 监听
在讲完了路由的注册,我们来聊聊路由的监听。
type serverHandler struct {
srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
HandleDefaultServeMuxrouterrouter
routerServeHTTP
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
...
path := req.URL.Path
if root := r.trees[req.Method]; root != nil {
if handle, ps, tsr := root.getValue(path, r.getParams); handle != nil {
if ps != nil {
handle(w, req, *ps)
r.putParams(ps)
} else {
handle(w, req, nil)
}
return
}
}
...
// Handle 404
if r.NotFound != nil {
r.NotFound.ServeHTTP(w, req)
} else {
http.NotFound(w, req)
}
}
getValue
pathHandle
ParamParam
type Param struct {
Key string
Value string
}
如果未找到相对应的路由,则调用后面的404方法。
5 处理
到了这一步,其实和以前的内容几乎一样了。
Handle
net/httpHandlerHandle
type Handle func(http.ResponseWriter, *http.Request, Params)