route.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. package restful
  2. // Copyright 2013 Ernest Micklei. All rights reserved.
  3. // Use of this source code is governed by a license
  4. // that can be found in the LICENSE file.
  5. import (
  6. "bytes"
  7. "net/http"
  8. "strings"
  9. )
  10. // RouteFunction declares the signature of a function that can be bound to a Route.
  11. type RouteFunction func(*Request, *Response)
  12. // Route binds a HTTP Method,Path,Consumes combination to a RouteFunction.
  13. type Route struct {
  14. Method string
  15. Produces []string
  16. Consumes []string
  17. Path string // webservice root path + described path
  18. Function RouteFunction
  19. Filters []FilterFunction
  20. // cached values for dispatching
  21. relativePath string
  22. pathParts []string
  23. pathExpr *pathExpression // cached compilation of relativePath as RegExp
  24. // documentation
  25. Doc string
  26. Notes string
  27. Operation string
  28. ParameterDocs []*Parameter
  29. ResponseErrors map[int]ResponseError
  30. ReadSample, WriteSample interface{} // structs that model an example request or response payload
  31. }
  32. // Initialize for Route
  33. func (r *Route) postBuild() {
  34. r.pathParts = tokenizePath(r.Path)
  35. }
  36. // Create Request and Response from their http versions
  37. func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) {
  38. params := r.extractParameters(httpRequest.URL.Path)
  39. wrappedRequest := NewRequest(httpRequest)
  40. wrappedRequest.pathParameters = params
  41. wrappedRequest.selectedRoutePath = r.Path
  42. wrappedResponse := NewResponse(httpWriter)
  43. wrappedResponse.requestAccept = httpRequest.Header.Get(HEADER_Accept)
  44. wrappedResponse.routeProduces = r.Produces
  45. return wrappedRequest, wrappedResponse
  46. }
  47. // dispatchWithFilters call the function after passing through its own filters
  48. func (r *Route) dispatchWithFilters(wrappedRequest *Request, wrappedResponse *Response) {
  49. if len(r.Filters) > 0 {
  50. chain := FilterChain{Filters: r.Filters, Target: r.Function}
  51. chain.ProcessFilter(wrappedRequest, wrappedResponse)
  52. } else {
  53. // unfiltered
  54. r.Function(wrappedRequest, wrappedResponse)
  55. }
  56. }
  57. // Return whether the mimeType matches to what this Route can produce.
  58. func (r Route) matchesAccept(mimeTypesWithQuality string) bool {
  59. parts := strings.Split(mimeTypesWithQuality, ",")
  60. for _, each := range parts {
  61. var withoutQuality string
  62. if strings.Contains(each, ";") {
  63. withoutQuality = strings.Split(each, ";")[0]
  64. } else {
  65. withoutQuality = each
  66. }
  67. // trim before compare
  68. withoutQuality = strings.Trim(withoutQuality, " ")
  69. if withoutQuality == "*/*" {
  70. return true
  71. }
  72. for _, producibleType := range r.Produces {
  73. if producibleType == "*/*" || producibleType == withoutQuality {
  74. return true
  75. }
  76. }
  77. }
  78. return false
  79. }
  80. // Return whether this Route can consume content with a type specified by mimeTypes (can be empty).
  81. func (r Route) matchesContentType(mimeTypes string) bool {
  82. if len(r.Consumes) == 0 {
  83. // did not specify what it can consume ; any media type (“*/*”) is assumed
  84. return true
  85. }
  86. if len(mimeTypes) == 0 {
  87. // idempotent methods with (most-likely or garanteed) empty content match missing Content-Type
  88. m := r.Method
  89. if m == "GET" || m == "HEAD" || m == "OPTIONS" || m == "DELETE" || m == "TRACE" {
  90. return true
  91. }
  92. // proceed with default
  93. mimeTypes = MIME_OCTET
  94. }
  95. parts := strings.Split(mimeTypes, ",")
  96. for _, each := range parts {
  97. var contentType string
  98. if strings.Contains(each, ";") {
  99. contentType = strings.Split(each, ";")[0]
  100. } else {
  101. contentType = each
  102. }
  103. // trim before compare
  104. contentType = strings.Trim(contentType, " ")
  105. for _, consumeableType := range r.Consumes {
  106. if consumeableType == "*/*" || consumeableType == contentType {
  107. return true
  108. }
  109. }
  110. }
  111. return false
  112. }
  113. // Extract the parameters from the request url path
  114. func (r Route) extractParameters(urlPath string) map[string]string {
  115. urlParts := tokenizePath(urlPath)
  116. pathParameters := map[string]string{}
  117. for i, key := range r.pathParts {
  118. var value string
  119. if i >= len(urlParts) {
  120. value = ""
  121. } else {
  122. value = urlParts[i]
  123. }
  124. if strings.HasPrefix(key, "{") { // path-parameter
  125. if colon := strings.Index(key, ":"); colon != -1 {
  126. // extract by regex
  127. regPart := key[colon+1 : len(key)-1]
  128. keyPart := key[1:colon]
  129. if regPart == "*" {
  130. pathParameters[keyPart] = untokenizePath(i, urlParts)
  131. break
  132. } else {
  133. pathParameters[keyPart] = value
  134. }
  135. } else {
  136. // without enclosing {}
  137. pathParameters[key[1:len(key)-1]] = value
  138. }
  139. }
  140. }
  141. return pathParameters
  142. }
  143. // Untokenize back into an URL path using the slash separator
  144. func untokenizePath(offset int, parts []string) string {
  145. var buffer bytes.Buffer
  146. for p := offset; p < len(parts); p++ {
  147. buffer.WriteString(parts[p])
  148. // do not end
  149. if p < len(parts)-1 {
  150. buffer.WriteString("/")
  151. }
  152. }
  153. return buffer.String()
  154. }
  155. // Tokenize an URL path using the slash separator ; the result does not have empty tokens
  156. func tokenizePath(path string) []string {
  157. if "/" == path {
  158. return []string{}
  159. }
  160. return strings.Split(strings.Trim(path, "/"), "/")
  161. }
  162. // for debugging
  163. func (r Route) String() string {
  164. return r.Method + " " + r.Path
  165. }