-
Notifications
You must be signed in to change notification settings - Fork 0
/
route.go
183 lines (145 loc) · 4.32 KB
/
route.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package router
import (
"net/http"
"regexp"
"strings"
)
// Route is a single entrypoint into the router.
type Route struct {
methods []string
path string
// rawHandler is the handler provided to the route as-is, i.e., before it has
// been transformed into an http.Handler
rawHandler interface{}
handler http.Handler
regex *regexp.Regexp
// The {} bits of a route
params []string
group *Group
router *Router
middleware []Middleware
}
// Middleware defines additional logic on a single route definition by wrapping the
// route's handler in extra layers of logic.
func (route *Route) Middleware(middleware ...Middleware) *Route {
route.middleware = append(route.middleware, middleware...)
return route
}
// NewRoute creates a new route definition for a given method, path and handler.
func NewRoute(methods []string, path string, handler interface{}) *Route {
return newHandlerRoute(methods, path, handler)
}
func newHandlerRoute(methods []string, path string, handler interface{}) *Route {
if path[0] != '/' {
path = "/" + path
}
r := &Route{
methods: methods,
path: path,
rawHandler: handler,
}
r.regex = r.calculateRouteRegex()
return r
}
func (route *Route) Serve(w http.ResponseWriter, r *http.Request) {
handler := route.handler
var mw []Middleware
mw = append(mw, route.middleware...)
if route.group != nil {
mw = append(mw, route.group.middleware...)
}
if route.router != nil {
mw = append(mw, route.router.middleware...)
}
for _, m := range mw {
handler = m(handler)
}
handler.ServeHTTP(w, r)
}
func (r *Route) buildHandler() {
r.handler = r.group.router.buildHandler(r.rawHandler)
}
// Get defines a new `GET` route.
func Get(path string, handler interface{}) *Route {
return NewRoute([]string{http.MethodGet}, path, handler)
}
// Post defines a new `POST` route.
func Post(path string, handler interface{}) *Route {
return NewRoute([]string{http.MethodPost}, path, handler)
}
// Put defines a new `PUT` route.
func Put(path string, handler interface{}) *Route {
return NewRoute([]string{http.MethodPut}, path, handler)
}
// Patch defines a new `PATCH` route.
func Patch(path string, handler interface{}) *Route {
return NewRoute([]string{http.MethodPatch}, path, handler)
}
// Delete defines a new `DELETE` route.
func Delete(path string, handler interface{}) *Route {
return NewRoute([]string{http.MethodDelete}, path, handler)
}
// Options defines a new `OPTIONS` route.
func Options(path string, handler interface{}) *Route {
return NewRoute([]string{http.MethodOptions}, path, handler)
}
// Match defines a new route that responds to multiple http verbs.
func Match(verbs []string, path string, handler interface{}) *Route {
return NewRoute(verbs, path, handler)
}
// Any defines a new route that responds to any http verb.
func Any(path string, handler interface{}) *Route {
verbs := []string{
http.MethodGet,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
http.MethodOptions,
}
return NewRoute(verbs, path, handler)
}
func Redirect(from string, to string) *Route {
redirect := func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, to, http.StatusPermanentRedirect)
}
return NewRoute([]string{http.MethodGet}, from, redirect)
}
// matches determines if the route matches the incoming request.
func (r *Route) matches(router *Router, req *http.Request) bool {
for _, v := range router.validators {
if !v.Matches(r, req) {
return false
}
}
return true
}
func (r *Route) Methods() []string {
return r.methods
}
func (r *Route) Regex() *regexp.Regexp {
return r.regex
}
func (r *Route) calculateRouteRegex() *regexp.Regexp {
fullURI := r.path
if r.group != nil {
fullURI = strings.Join([]string{r.group.prefix, r.path}, "")
if fullURI[0] != '/' {
fullURI = "/" + fullURI
}
}
if !strings.ContainsAny(fullURI, "{}") {
return regexp.MustCompile("^" + fullURI + "$")
}
r.path = r.normalizeParamaterizedPath(r.path)
rx := regexp.MustCompile("{([^}:]+):?([^}]+)?}")
res2 := rx.FindAllStringSubmatch(r.path, -1)
for _, v := range res2 {
r.params = append(r.params, v[1])
}
return regexp.MustCompile("^" + rx.ReplaceAllString(r.path, "(?P<$1>$2)") + "$")
}
func (r *Route) normalizeParamaterizedPath(path string) string {
regex := regexp.MustCompile("{([^:}]+)}")
return regex.ReplaceAllString(path, "{$1:.+}")
}