-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathroutes.go
159 lines (144 loc) · 4.07 KB
/
routes.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
package rata
import (
"fmt"
"net/http"
"net/url"
"path"
"runtime"
"strings"
)
// Params map path keys to values. For example, if your route has the path pattern:
// /person/:person_id/pets/:pet_type
// Then a correct Params map would lool like:
// router.Params{
// "person_id": "123",
// "pet_type": "cats",
// }
type Params map[string]string
// A Route defines properties of an HTTP endpoint. At runtime, the router will
// associate each Route with a http.Handler object, and use the Route properties
// to determine which Handler should be invoked.
//
// Currently, the properties used for matching are Method and Path.
//
// Method can be one of the following:
// GET HEAD POST PUT PATCH DELETE CONNECT OPTIONS TRACE
//
// Path conforms to Pat-style pattern matching. The following docs are taken from
// http://godoc.org/github.com/bmizerany/pat#PatternServeMux
//
// Path Patterns may contain literals or captures. Capture names start with a colon
// and consist of letters A-Z, a-z, _, and 0-9. The rest of the pattern
// matches literally. The portion of the URL matching each name ends with an
// occurrence of the character in the pattern immediately following the name,
// or a /, whichever comes first. It is possible for a name to match the empty
// string.
//
// Example pattern with one capture:
// /hello/:name
// Will match:
// /hello/blake
// /hello/keith
// Will not match:
// /hello/blake/
// /hello/blake/foo
// /foo
// /foo/bar
//
// Example 2:
// /hello/:name/
// Will match:
// /hello/blake/
// /hello/keith/foo
// /hello/blake
// /hello/keith
// Will not match:
// /foo
// /foo/bar
type Route struct {
// Name is a key specifying which HTTP handler the router
// should associate with the endpoint at runtime.
Name string
// Method is any valid HTTP method
Method string
// Path contains a path pattern
Path string
}
// CreatePath combines the route's path pattern with a Params map
// to produce a valid path.
func (r Route) CreatePath(params Params) (string, error) {
components := strings.Split(r.Path, "/")
for i, c := range components {
if len(c) == 0 {
continue
}
if c[0] == ':' {
val, ok := params[c[1:]]
if !ok {
return "", fmt.Errorf("missing param %s", c)
}
components[i] = url.PathEscape(val)
}
}
u, err := url.Parse(strings.Join(components, "/"))
if err != nil {
return "", err
}
return u.String(), nil
}
// CreateURL combines the route's path pattern with a Host
// and a Params map to produce a valid URL.
func (r Route) createURL(host string, params Params) (*url.URL, error) {
u, err := url.Parse(host)
if err != nil {
return nil, err
}
path := path.Join(u.Path, r.Path)
components := strings.Split(path, "/")
rawComponents := make([]string, len(components))
copy(rawComponents, components)
for i, c := range components {
if len(c) == 0 {
continue
}
val := c
if c[0] == ':' {
var ok bool
val, ok = params[c[1:]]
if !ok {
return nil, fmt.Errorf("missing param %s", c)
}
components[i] = val
rawComponents[i] = url.PathEscape(val)
}
}
u.Path = strings.Join(components, "/")
u.RawPath = strings.Join(rawComponents, "/")
return u, nil
}
// Routes is a Route collection.
type Routes []Route
// Route looks up a Route by it's Handler key.
func (r Routes) FindRouteByName(name string) (Route, bool) {
for _, route := range r {
if route.Name == name {
return route, true
}
}
return Route{}, false
}
// Path looks up a Route by it's Handler key and computes it's path
// with a given Params map.
func (r Routes) CreatePathForRoute(name string, params Params) (string, error) {
route, ok := r.FindRouteByName(name)
if !ok {
return "", fmt.Errorf("No route exists with the name %", name)
}
return route.CreatePath(params)
}
// Router is deprecated, please use router.NewRouter() instead
func (r Routes) Router(handlers Handlers) (http.Handler, error) {
_, file, line, _ := runtime.Caller(1)
fmt.Printf("\n\033[0;35m%s\033[0m%s:%d:%s\n", "WARNING:", file, line, " Routes.Router() is deprecated, please use router.NewRouter() instead")
return NewRouter(r, handlers)
}