forked from emicklei/go-restful
/
route.go
114 lines (101 loc) · 3.19 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
// Copyright 2012 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
package restful
import (
"net/http"
"strings"
)
// Signature of a function that can be bound to a Route.
type RouteFunction func(*Request, *Response)
// Route binds a HTTP Method,Path,Consumes combination to a RouteFunction.
type Route struct {
Method string
Produces []string
Consumes []string
Path string
Function RouteFunction
Filters []FilterFunction
// cached values for dispatching
relativePath string
pathParts []string
pathExpr *pathExpression // cached compilation of relativePath as RegExp
// documentation
Doc string
ParameterDocs []*Parameter
ReadSample, WriteSample interface{} // structs that model an example request or response payload
}
// Initialize for Route
func (self *Route) postBuild() {
self.pathParts = tokenizePath(self.Path)
}
// Create Request and Response from their http versions
func (self *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) {
params := self.extractParameters(httpRequest.URL.Path)
accept := httpRequest.Header.Get(HEADER_Accept)
wrappedRequest := &Request{httpRequest, params}
wrappedResponse := &Response{httpWriter, accept, self.Produces}
return wrappedRequest, wrappedResponse
}
// Extract any path parameters from the the request URL path and call the function
func (self *Route) dispatch(wrappedRequest *Request, wrappedResponse *Response) {
if len(self.Filters) > 0 {
chain := FilterChain{Filters: self.Filters, Target: self.Function}
chain.ProcessFilter(wrappedRequest, wrappedResponse)
} else {
// unfiltered
self.Function(wrappedRequest, wrappedResponse)
}
}
// Return whether the mimeType matches to what this Route can produce.
func (self Route) matchesAccept(mimeTypesWithQuality string) bool {
parts := strings.Split(mimeTypesWithQuality, ",")
for _, each := range parts {
withoutQuality := strings.Split(each, ";")[0]
if withoutQuality == "*/*" {
return true
}
for _, other := range self.Produces {
if other == withoutQuality {
return true
}
}
}
return false
}
// Return whether the mimeType matches to what this Route can consume.
func (self Route) matchesContentType(mimeTypes string) bool {
parts := strings.Split(mimeTypes, ",")
for _, each := range parts {
for _, other := range self.Consumes {
if other == "*/*" || other == each {
return true
}
}
}
return false
}
// Extract the parameters from the request url path
func (self Route) extractParameters(urlPath string) map[string]string {
urlParts := tokenizePath(urlPath)
pathParameters := map[string]string{}
for i, key := range self.pathParts {
var value string
if i >= len(urlParts) {
value = ""
} else {
value = urlParts[i]
}
if strings.HasPrefix(key, "{") { // path-parameter
pathParameters[strings.Trim(key, "{}")] = value
}
}
return pathParameters
}
// Tokenize an URL path using the slash separator ; the result does not have empty tokens
func tokenizePath(path string) []string {
if "/" == path {
return []string{}
}
return strings.Split(strings.Trim(path, "/"), "/")
}