forked from DanieleDaccurso/wrouter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
router.go
180 lines (153 loc) · 5.33 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
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
package wrouter
import (
"github.com/owtorg/clitable"
"github.com/owtorg/events"
"io"
"net/http"
"reflect"
"strconv"
)
var AllowedMethods = []string{"get", "post", "put", "patch", "head", "trace", "connect", "options", "delete"}
// Add an HTTP Method to the routes
// Allowed methods: "get", "post", "put", "patch", "head", "trace", "connect", "options", "delete"
func (r *Route) AddMethod(method string) {
r.Methods = append(r.Methods, method)
}
// Router represents one implementation of the http.Handler interface
type Router struct {
routes []*Route
injectors []Injector
// Eventcollections
// See: github.com/owtorg/events
preRequest *events.EventCollection
postRequest *events.EventCollection
// Configuration contains the router configuration
Configuration *Configuration
RouteResolver RouteResolver
RequestResolver RequestResolver
}
// Create a new Router
func NewRouter() *Router {
r := new(Router)
r.preRequest = new(events.EventCollection)
r.postRequest = new(events.EventCollection)
r.Configuration = createDefaultConfiguration()
r.RouteResolver = newRouteResolver(r.Configuration)
r.RequestResolver = newRqResolver(r)
return r
}
// ServeHTTP satisfies the http.Handler interface, so that this Router can be used as the
// second parameter of http.ListenAndServe
func (r *Router) ServeHTTP(w http.ResponseWriter, h *http.Request) {
// Execute PreRequestEvents if any
if r.preRequest.Len() != 0 {
ctx := createPreRequestEventContext(h, w)
events.DispatchEvents(r.preRequest, ctx)
}
route := r.findRequestRoute(h)
if route == nil {
// @TODO: Implement check if an ErrorController exists
// Define Specifications for ErrorController (ex: StatusNotFoundAction??)
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not Found"))
return
}
r.callRoute(route, w, h)
}
// PrintRoutes will print out all routes to io.Writer
func (r *Router) PrintRoutes(writer io.Writer) {
t := clitable.New()
t.AddRow("ID", "METHODS", "PATH")
for i, route := range r.routes {
ms := ""
for _, me := range route.Methods {
ms += me + " "
}
t.AddRow(strconv.Itoa(i), ms, route.Path)
}
t.Fprint(writer)
}
// AddController will add a new controller to the router.
func (r *Router) AddController(controller interface{}) {
routes, err := r.RouteResolver.Resolve(controller)
if err != nil {
panic(err)
}
if len(routes) > 0 {
for i := 0; i < len(routes); i++ {
r.AddRoute(routes[i])
}
}
}
// AddRoute will add a new Route to a controller.
func (r *Router) AddRoute(route *Route) {
r.routes = append(r.routes, route)
}
// AddInjector will append an Injector to the list of Injectors in this router.
// It will automatically be the last executed injector as the first injector to support a certain
// type, will be the one who serves the value.
func (r *Router) AddInjector(in Injector) {
r.injectors = append(r.injectors, in)
}
// AddPreRequestEvent will add a PreRequestEvent with a given priority. It will return an error
// if the selected priority is already taken.
func (r *Router) AddPreRequestEvent(ev PreRequestEvent, priority uint) {
r.preRequest.AddEvent(ev, priority)
}
// AddPostRequestEvent will add a PostRequestEvent with a given priority. It will return an error
// if the selected priority is already taken.
func (r *Router) AddPostRequestEvent(ev PostRequestEvent, priority uint) {
r.postRequest.AddEvent(ev, priority)
}
// AppendPreRequestEvent will append a PreRequestEvent at the end of the current PreRequestEvent
// queue. If you want to set a priority, see AddPreRequestEvent
func (r *Router) AppendPreRequestEvent(ev PreRequestEvent) {
r.preRequest.AppendEvent(ev)
}
// AppendPostRequestEvent will append a PreRequestEvent at the end of the current AddPostRequestEvent
// queue. If you want to set a priority, see AddPostRequestEvent
func (r *Router) AppendPostRequestEvent(ev PostRequestEvent) {
r.postRequest.AppendEvent(ev)
}
func (r *Router) findRequestRoute(h *http.Request) *Route {
return r.RequestResolver.Resolve(h)
}
func (r *Router) callRoute(route *Route, w http.ResponseWriter, h *http.Request) {
values := make([]reflect.Value, 0)
ctx := createInjectorContext(h, route, r, w)
// Currently, every controller action needs to be part of a struct, therefore
// the first argument of the method, is the struct itself. This happens implicit
// when a method is defined as func (s *struct) doit()
values = append(values, reflect.ValueOf(route.Controller))
// argument resolving switch is only called, if a method has more than one argument
if route.RMethod.Type.NumIn() > 1 {
// Call controller method and inject arguents by reflection
for i := 1; i < route.RMethod.Type.NumIn(); i++ {
arg := route.RMethod.Type.In(i)
switch arg.String() {
case "http.ResponseWriter":
values = append(values, reflect.ValueOf(w))
case "*http.Request":
values = append(values, reflect.ValueOf(h))
default:
values = append(values, reflect.ValueOf(r.inject(arg.String(), ctx)))
}
}
}
ret := route.RMethod.Func.Call(values)
// Execute PostRequest events
if r.postRequest.Len() != 0 {
ctx := createPostRequestEventContext(h, w, ret)
events.DispatchEvents(r.postRequest, ctx)
}
}
func (r *Router) inject(t string, ctx *InjectorContext) interface{} {
if len(r.injectors) != 0 {
for _, injector := range r.injectors {
if injector.Supports(t) {
return injector.Get(ctx)
}
}
}
return nil
}