该篇文章将会一步一步教你如何使用Go编写Http Router

知识点:

  • Trie(前缀树):保存PathPattern的数据结构
  • Go Http 相关API

定义基本的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
type Handle func(http.ResponseWriter, *http.Request, url.Values)
type Router struct {
tree *node
}
type node struct {
children []*node
component string
isNamedParam bool
methods map[string]Handle
}
func (n *node) addNode(method, path string, handler Handle) {
components := strings.Split(path, "/")[1:]
count := len(components)
for {
aNode, component := n.traverse(components, nil)
if aNode.component == component && count == 1 {
aNode.methods[method] = handler
return
}
newNode := node{component: component, isNamedParam: false, methods: make(map[string]Handle)}
if len(component) > 0 && component[0] == ':' {
newNode.isNamedParam = true
}
if count == 1 {
newNode.methods[method] = handler
}
aNode.children = append(aNode.children, &newNode)
count--
if count == 0 {
break
}
}
}
func (n *node) traverse(components []string, params url.Values) (*node, string) {
component := components[0]
if len(n.children) > 0 {
for _, child := range n.children {
if component == child.component || child.isNamedParam {
if child.isNamedParam && params != nil {
params.Add(child.component[1:], component)
}
next := components[1:]
if len(next) > 0 {
return child.traverse(next, params)
} else {
return child, component
}
}
}
}
return n, component
}

编写构建New函数

1
2
3
4
func New() *Router {
node := node{component: "/", isNamedParam: false, methods: make(map[string]Handle)}
return &Router{tree: &node}
}

编写添加PathPattern函数

1
2
3
4
5
6
func (r *Router) Handle(method, path string, handler Handle) {
if path[0] != '/' {
panic("Path has to start with a /.")
}
r.tree.addNode(method, path, handler)
}

编写统一接入函数

1
2
3
4
5
6
7
8
9
10
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
req.ParseForm()
params := req.Form
node, _ := r.Tree.traverse(strings.Split(req.URL.Path, "/")[1:], params)
if handler := node.methods[req.Method]; handler != nil {
handler(w, req, params)
} else {
w.WriteHeader(http.StatusNotFound)
}
}

使用Http Router

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
r := New()
r.Handle("GET", "/", Index)
r.Handle("GET", "/users", Users)
r.Handle("GET", "/users/:name", UserShow)
http.ListenAndServe(":8080", r)
}
func Index(w http.ResponseWriter, r *http.Request, params url.Values) {
fmt.Fprint(w, "Index!\n")
}
func Users(w http.ResponseWriter, r *http.Request, params url.Values) {
fmt.Fprint(w, "Users!\n")
}
func UserShow(w http.ResponseWriter, r *http.Request, params url.Values) {
fmt.Fprintf(w, "Hi %s", params["name"])
}