route_builder.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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. "fmt"
  7. "os"
  8. "reflect"
  9. "runtime"
  10. "strings"
  11. "sync/atomic"
  12. "github.com/emicklei/go-restful/log"
  13. )
  14. // RouteBuilder is a helper to construct Routes.
  15. type RouteBuilder struct {
  16. rootPath string
  17. currentPath string
  18. produces []string
  19. consumes []string
  20. httpMethod string // required
  21. function RouteFunction // required
  22. filters []FilterFunction
  23. typeNameHandleFunc TypeNameHandleFunction // required
  24. // documentation
  25. doc string
  26. notes string
  27. operation string
  28. readSample, writeSample interface{}
  29. parameters []*Parameter
  30. errorMap map[int]ResponseError
  31. metadata map[string]interface{}
  32. }
  33. // Do evaluates each argument with the RouteBuilder itself.
  34. // This allows you to follow DRY principles without breaking the fluent programming style.
  35. // Example:
  36. // ws.Route(ws.DELETE("/{name}").To(t.deletePerson).Do(Returns200, Returns500))
  37. //
  38. // func Returns500(b *RouteBuilder) {
  39. // b.Returns(500, "Internal Server Error", restful.ServiceError{})
  40. // }
  41. func (b *RouteBuilder) Do(oneArgBlocks ...func(*RouteBuilder)) *RouteBuilder {
  42. for _, each := range oneArgBlocks {
  43. each(b)
  44. }
  45. return b
  46. }
  47. // To bind the route to a function.
  48. // If this route is matched with the incoming Http Request then call this function with the *Request,*Response pair. Required.
  49. func (b *RouteBuilder) To(function RouteFunction) *RouteBuilder {
  50. b.function = function
  51. return b
  52. }
  53. // Method specifies what HTTP method to match. Required.
  54. func (b *RouteBuilder) Method(method string) *RouteBuilder {
  55. b.httpMethod = method
  56. return b
  57. }
  58. // Produces specifies what MIME types can be produced ; the matched one will appear in the Content-Type Http header.
  59. func (b *RouteBuilder) Produces(mimeTypes ...string) *RouteBuilder {
  60. b.produces = mimeTypes
  61. return b
  62. }
  63. // Consumes specifies what MIME types can be consumes ; the Accept Http header must matched any of these
  64. func (b *RouteBuilder) Consumes(mimeTypes ...string) *RouteBuilder {
  65. b.consumes = mimeTypes
  66. return b
  67. }
  68. // Path specifies the relative (w.r.t WebService root path) URL path to match. Default is "/".
  69. func (b *RouteBuilder) Path(subPath string) *RouteBuilder {
  70. b.currentPath = subPath
  71. return b
  72. }
  73. // Doc tells what this route is all about. Optional.
  74. func (b *RouteBuilder) Doc(documentation string) *RouteBuilder {
  75. b.doc = documentation
  76. return b
  77. }
  78. // A verbose explanation of the operation behavior. Optional.
  79. func (b *RouteBuilder) Notes(notes string) *RouteBuilder {
  80. b.notes = notes
  81. return b
  82. }
  83. // Reads tells what resource type will be read from the request payload. Optional.
  84. // A parameter of type "body" is added ,required is set to true and the dataType is set to the qualified name of the sample's type.
  85. func (b *RouteBuilder) Reads(sample interface{}) *RouteBuilder {
  86. fn := b.typeNameHandleFunc
  87. if fn == nil {
  88. fn = reflectTypeName
  89. }
  90. typeAsName := fn(sample)
  91. b.readSample = sample
  92. bodyParameter := &Parameter{&ParameterData{Name: "body"}}
  93. bodyParameter.beBody()
  94. bodyParameter.Required(true)
  95. bodyParameter.DataType(typeAsName)
  96. b.Param(bodyParameter)
  97. return b
  98. }
  99. // ParameterNamed returns a Parameter already known to the RouteBuilder. Returns nil if not.
  100. // Use this to modify or extend information for the Parameter (through its Data()).
  101. func (b RouteBuilder) ParameterNamed(name string) (p *Parameter) {
  102. for _, each := range b.parameters {
  103. if each.Data().Name == name {
  104. return each
  105. }
  106. }
  107. return p
  108. }
  109. // Writes tells what resource type will be written as the response payload. Optional.
  110. func (b *RouteBuilder) Writes(sample interface{}) *RouteBuilder {
  111. b.writeSample = sample
  112. return b
  113. }
  114. // Param allows you to document the parameters of the Route. It adds a new Parameter (does not check for duplicates).
  115. func (b *RouteBuilder) Param(parameter *Parameter) *RouteBuilder {
  116. if b.parameters == nil {
  117. b.parameters = []*Parameter{}
  118. }
  119. b.parameters = append(b.parameters, parameter)
  120. return b
  121. }
  122. // Operation allows you to document what the actual method/function call is of the Route.
  123. // Unless called, the operation name is derived from the RouteFunction set using To(..).
  124. func (b *RouteBuilder) Operation(name string) *RouteBuilder {
  125. b.operation = name
  126. return b
  127. }
  128. // ReturnsError is deprecated, use Returns instead.
  129. func (b *RouteBuilder) ReturnsError(code int, message string, model interface{}) *RouteBuilder {
  130. log.Print("ReturnsError is deprecated, use Returns instead.")
  131. return b.Returns(code, message, model)
  132. }
  133. // Returns allows you to document what responses (errors or regular) can be expected.
  134. // The model parameter is optional ; either pass a struct instance or use nil if not applicable.
  135. func (b *RouteBuilder) Returns(code int, message string, model interface{}) *RouteBuilder {
  136. err := ResponseError{
  137. Code: code,
  138. Message: message,
  139. Model: model,
  140. IsDefault: false,
  141. }
  142. // lazy init because there is no NewRouteBuilder (yet)
  143. if b.errorMap == nil {
  144. b.errorMap = map[int]ResponseError{}
  145. }
  146. b.errorMap[code] = err
  147. return b
  148. }
  149. // DefaultReturns is a special Returns call that sets the default of the response ; the code is zero.
  150. func (b *RouteBuilder) DefaultReturns(message string, model interface{}) *RouteBuilder {
  151. b.Returns(0, message, model)
  152. // Modify the ResponseError just added/updated
  153. re := b.errorMap[0]
  154. // errorMap is initialized
  155. b.errorMap[0] = ResponseError{
  156. Code: re.Code,
  157. Message: re.Message,
  158. Model: re.Model,
  159. IsDefault: true,
  160. }
  161. return b
  162. }
  163. // Metadata adds or updates a key=value pair to the metadata map.
  164. func (b *RouteBuilder) Metadata(key string, value interface{}) *RouteBuilder {
  165. if b.metadata == nil {
  166. b.metadata = map[string]interface{}{}
  167. }
  168. b.metadata[key] = value
  169. return b
  170. }
  171. // ResponseError represents a response; not necessarily an error.
  172. type ResponseError struct {
  173. Code int
  174. Message string
  175. Model interface{}
  176. IsDefault bool
  177. }
  178. func (b *RouteBuilder) servicePath(path string) *RouteBuilder {
  179. b.rootPath = path
  180. return b
  181. }
  182. // Filter appends a FilterFunction to the end of filters for this Route to build.
  183. func (b *RouteBuilder) Filter(filter FilterFunction) *RouteBuilder {
  184. b.filters = append(b.filters, filter)
  185. return b
  186. }
  187. // If no specific Route path then set to rootPath
  188. // If no specific Produces then set to rootProduces
  189. // If no specific Consumes then set to rootConsumes
  190. func (b *RouteBuilder) copyDefaults(rootProduces, rootConsumes []string) {
  191. if len(b.produces) == 0 {
  192. b.produces = rootProduces
  193. }
  194. if len(b.consumes) == 0 {
  195. b.consumes = rootConsumes
  196. }
  197. }
  198. // typeNameHandler sets the function that will convert types to strings in the parameter
  199. // and model definitions.
  200. func (b *RouteBuilder) typeNameHandler(handler TypeNameHandleFunction) *RouteBuilder {
  201. b.typeNameHandleFunc = handler
  202. return b
  203. }
  204. // Build creates a new Route using the specification details collected by the RouteBuilder
  205. func (b *RouteBuilder) Build() Route {
  206. pathExpr, err := newPathExpression(b.currentPath)
  207. if err != nil {
  208. log.Printf("[restful] Invalid path:%s because:%v", b.currentPath, err)
  209. os.Exit(1)
  210. }
  211. if b.function == nil {
  212. log.Printf("[restful] No function specified for route:" + b.currentPath)
  213. os.Exit(1)
  214. }
  215. operationName := b.operation
  216. if len(operationName) == 0 && b.function != nil {
  217. // extract from definition
  218. operationName = nameOfFunction(b.function)
  219. }
  220. route := Route{
  221. Method: b.httpMethod,
  222. Path: concatPath(b.rootPath, b.currentPath),
  223. Produces: b.produces,
  224. Consumes: b.consumes,
  225. Function: b.function,
  226. Filters: b.filters,
  227. relativePath: b.currentPath,
  228. pathExpr: pathExpr,
  229. Doc: b.doc,
  230. Notes: b.notes,
  231. Operation: operationName,
  232. ParameterDocs: b.parameters,
  233. ResponseErrors: b.errorMap,
  234. ReadSample: b.readSample,
  235. WriteSample: b.writeSample,
  236. Metadata: b.metadata}
  237. route.postBuild()
  238. return route
  239. }
  240. func concatPath(path1, path2 string) string {
  241. return strings.TrimRight(path1, "/") + "/" + strings.TrimLeft(path2, "/")
  242. }
  243. var anonymousFuncCount int32
  244. // nameOfFunction returns the short name of the function f for documentation.
  245. // It uses a runtime feature for debugging ; its value may change for later Go versions.
  246. func nameOfFunction(f interface{}) string {
  247. fun := runtime.FuncForPC(reflect.ValueOf(f).Pointer())
  248. tokenized := strings.Split(fun.Name(), ".")
  249. last := tokenized[len(tokenized)-1]
  250. last = strings.TrimSuffix(last, ")·fm") // < Go 1.5
  251. last = strings.TrimSuffix(last, ")-fm") // Go 1.5
  252. last = strings.TrimSuffix(last, "·fm") // < Go 1.5
  253. last = strings.TrimSuffix(last, "-fm") // Go 1.5
  254. if last == "func1" { // this could mean conflicts in API docs
  255. val := atomic.AddInt32(&anonymousFuncCount, 1)
  256. last = "func" + fmt.Sprintf("%d", val)
  257. atomic.StoreInt32(&anonymousFuncCount, val)
  258. }
  259. return last
  260. }