-
Notifications
You must be signed in to change notification settings - Fork 0
/
router.go
134 lines (116 loc) · 3.55 KB
/
router.go
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package garson
import (
"context"
"errors"
"net/http"
"regexp"
"strings"
)
// Router struct
type Router struct {
Routes []*Route
}
// Params is the type of paramters passed to a route
type Params map[string]string
// Route struct just contains the method, path and Handle func
type Route struct {
Method string
Path *regexp.Regexp
RegisteredParams []string
Handler http.HandlerFunc
}
var paramsRegexp = regexp.MustCompile(`:(\w+)`)
// Try loops through the routes array to find the requested route
// If the route is not found, it returns http.NotFound error
func (r *Router) Try(path string, method string) (http.HandlerFunc, Params, error) {
for _, route := range r.Routes {
if route.Method == method {
match := route.Path.MatchString(path)
if match == false {
continue
}
params := Params{}
// check if this route has registered params, and then parse them
if len(route.RegisteredParams) > 0 {
params = parseParams(route, path)
}
return route.Handler, params, nil
}
}
return nil, Params{}, errors.New("Route not found")
}
// add is a shortcut func to append new routes to the routes array
// and it extracts the params from the registered url
// used in router.Get(), router.Post(), router.Put(), router.Delete()
func (r *Router) add(method string, path string, handler http.HandlerFunc) {
route := &Route{}
route.Method = method
path = "^" + path + "$"
route.Handler = handler
if strings.Contains(path, ":") {
matches := paramsRegexp.FindAllStringSubmatch(path, -1)
if matches != nil {
for _, v := range matches {
route.RegisteredParams = append(route.RegisteredParams, v[1])
// remove the :params from the url path and replace them with regex
path = strings.Replace(path, v[0], `(\w+)`, 1)
}
}
}
compiledPath, err := regexp.Compile(path)
if err != nil {
panic(err)
}
route.Path = compiledPath
r.Routes = append(r.Routes, route)
}
// Connect adds a CONNECT method to routes
func (r *Router) Connect(path string, handler http.HandlerFunc) {
r.add("CONNECT", path, handler)
}
// Get adds a GET method to routes
func (r *Router) Get(path string, handler http.HandlerFunc) {
r.add("GET", path, handler)
}
// Post adds a POST method to routes
func (r *Router) Post(path string, handler http.HandlerFunc) {
r.add("POST", path, handler)
}
// Put adds a PUT method to routes
func (r *Router) Put(path string, handler http.HandlerFunc) {
r.add("PUT", path, handler)
}
// Delete adds a DELETE method to routes
func (r *Router) Delete(path string, handler http.HandlerFunc) {
r.add("DELETE", path, handler)
}
// Head adds a HEAD method to routes
func (r *Router) Head(path string, handler http.HandlerFunc) {
r.add("HEAD", path, handler)
}
// Options adds an OPTIONS method to routes
func (r *Router) Options(path string, handler http.HandlerFunc) {
r.add("OPTIONS", path, handler)
}
// ServeHTTP implementats of the http.Handler interface
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
handler, params, err := r.Try(req.URL.Path, req.Method)
if err != nil {
http.NotFound(w, req)
return
}
ctx := context.WithValue(req.Context(), "route_params", params)
// execute the router handler
handler(w, req.WithContext(ctx))
}
// parseParams parses the request url against route.Path and returns
// a Params object
func parseParams(route *Route, path string) Params {
matches := route.Path.FindAllStringSubmatch(path, -1)
params := Params{}
matchedParams := matches[0][1:]
for k, v := range matchedParams {
params[route.RegisteredParams[k]] = v
}
return params
}