web_service.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. package restful
  2. import (
  3. "errors"
  4. "os"
  5. "sync"
  6. "github.com/emicklei/go-restful/log"
  7. )
  8. // Copyright 2013 Ernest Micklei. All rights reserved.
  9. // Use of this source code is governed by a license
  10. // that can be found in the LICENSE file.
  11. // WebService holds a collection of Route values that bind a Http Method + URL Path to a function.
  12. type WebService struct {
  13. rootPath string
  14. pathExpr *pathExpression // cached compilation of rootPath as RegExp
  15. routes []Route
  16. produces []string
  17. consumes []string
  18. pathParameters []*Parameter
  19. filters []FilterFunction
  20. documentation string
  21. apiVersion string
  22. dynamicRoutes bool
  23. // protects 'routes' if dynamic routes are enabled
  24. routesLock sync.RWMutex
  25. }
  26. func (w *WebService) SetDynamicRoutes(enable bool) {
  27. w.dynamicRoutes = enable
  28. }
  29. // compilePathExpression ensures that the path is compiled into a RegEx for those routers that need it.
  30. func (w *WebService) compilePathExpression() {
  31. compiled, err := newPathExpression(w.rootPath)
  32. if err != nil {
  33. log.Printf("[restful] invalid path:%s because:%v", w.rootPath, err)
  34. os.Exit(1)
  35. }
  36. w.pathExpr = compiled
  37. }
  38. // ApiVersion sets the API version for documentation purposes.
  39. func (w *WebService) ApiVersion(apiVersion string) *WebService {
  40. w.apiVersion = apiVersion
  41. return w
  42. }
  43. // Version returns the API version for documentation purposes.
  44. func (w *WebService) Version() string { return w.apiVersion }
  45. // Path specifies the root URL template path of the WebService.
  46. // All Routes will be relative to this path.
  47. func (w *WebService) Path(root string) *WebService {
  48. w.rootPath = root
  49. if len(w.rootPath) == 0 {
  50. w.rootPath = "/"
  51. }
  52. w.compilePathExpression()
  53. return w
  54. }
  55. // Param adds a PathParameter to document parameters used in the root path.
  56. func (w *WebService) Param(parameter *Parameter) *WebService {
  57. if w.pathParameters == nil {
  58. w.pathParameters = []*Parameter{}
  59. }
  60. w.pathParameters = append(w.pathParameters, parameter)
  61. return w
  62. }
  63. // PathParameter creates a new Parameter of kind Path for documentation purposes.
  64. // It is initialized as required with string as its DataType.
  65. func (w *WebService) PathParameter(name, description string) *Parameter {
  66. return PathParameter(name, description)
  67. }
  68. // PathParameter creates a new Parameter of kind Path for documentation purposes.
  69. // It is initialized as required with string as its DataType.
  70. func PathParameter(name, description string) *Parameter {
  71. p := &Parameter{&ParameterData{Name: name, Description: description, Required: true, DataType: "string"}}
  72. p.bePath()
  73. return p
  74. }
  75. // QueryParameter creates a new Parameter of kind Query for documentation purposes.
  76. // It is initialized as not required with string as its DataType.
  77. func (w *WebService) QueryParameter(name, description string) *Parameter {
  78. return QueryParameter(name, description)
  79. }
  80. // QueryParameter creates a new Parameter of kind Query for documentation purposes.
  81. // It is initialized as not required with string as its DataType.
  82. func QueryParameter(name, description string) *Parameter {
  83. p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}}
  84. p.beQuery()
  85. return p
  86. }
  87. // BodyParameter creates a new Parameter of kind Body for documentation purposes.
  88. // It is initialized as required without a DataType.
  89. func (w *WebService) BodyParameter(name, description string) *Parameter {
  90. return BodyParameter(name, description)
  91. }
  92. // BodyParameter creates a new Parameter of kind Body for documentation purposes.
  93. // It is initialized as required without a DataType.
  94. func BodyParameter(name, description string) *Parameter {
  95. p := &Parameter{&ParameterData{Name: name, Description: description, Required: true}}
  96. p.beBody()
  97. return p
  98. }
  99. // HeaderParameter creates a new Parameter of kind (Http) Header for documentation purposes.
  100. // It is initialized as not required with string as its DataType.
  101. func (w *WebService) HeaderParameter(name, description string) *Parameter {
  102. return HeaderParameter(name, description)
  103. }
  104. // HeaderParameter creates a new Parameter of kind (Http) Header for documentation purposes.
  105. // It is initialized as not required with string as its DataType.
  106. func HeaderParameter(name, description string) *Parameter {
  107. p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}}
  108. p.beHeader()
  109. return p
  110. }
  111. // FormParameter creates a new Parameter of kind Form (using application/x-www-form-urlencoded) for documentation purposes.
  112. // It is initialized as required with string as its DataType.
  113. func (w *WebService) FormParameter(name, description string) *Parameter {
  114. return FormParameter(name, description)
  115. }
  116. // FormParameter creates a new Parameter of kind Form (using application/x-www-form-urlencoded) for documentation purposes.
  117. // It is initialized as required with string as its DataType.
  118. func FormParameter(name, description string) *Parameter {
  119. p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}}
  120. p.beForm()
  121. return p
  122. }
  123. // Route creates a new Route using the RouteBuilder and add to the ordered list of Routes.
  124. func (w *WebService) Route(builder *RouteBuilder) *WebService {
  125. w.routesLock.Lock()
  126. defer w.routesLock.Unlock()
  127. builder.copyDefaults(w.produces, w.consumes)
  128. w.routes = append(w.routes, builder.Build())
  129. return w
  130. }
  131. // RemoveRoute removes the specified route, looks for something that matches 'path' and 'method'
  132. func (w *WebService) RemoveRoute(path, method string) error {
  133. if !w.dynamicRoutes {
  134. return errors.New("dynamic routes are not enabled.")
  135. }
  136. w.routesLock.Lock()
  137. defer w.routesLock.Unlock()
  138. newRoutes := make([]Route, (len(w.routes) - 1))
  139. current := 0
  140. for ix := range w.routes {
  141. if w.routes[ix].Method == method && w.routes[ix].Path == path {
  142. continue
  143. }
  144. newRoutes[current] = w.routes[ix]
  145. current = current + 1
  146. }
  147. w.routes = newRoutes
  148. return nil
  149. }
  150. // Method creates a new RouteBuilder and initialize its http method
  151. func (w *WebService) Method(httpMethod string) *RouteBuilder {
  152. return new(RouteBuilder).servicePath(w.rootPath).Method(httpMethod)
  153. }
  154. // Produces specifies that this WebService can produce one or more MIME types.
  155. // Http requests must have one of these values set for the Accept header.
  156. func (w *WebService) Produces(contentTypes ...string) *WebService {
  157. w.produces = contentTypes
  158. return w
  159. }
  160. // Consumes specifies that this WebService can consume one or more MIME types.
  161. // Http requests must have one of these values set for the Content-Type header.
  162. func (w *WebService) Consumes(accepts ...string) *WebService {
  163. w.consumes = accepts
  164. return w
  165. }
  166. // Routes returns the Routes associated with this WebService
  167. func (w *WebService) Routes() []Route {
  168. if !w.dynamicRoutes {
  169. return w.routes
  170. }
  171. // Make a copy of the array to prevent concurrency problems
  172. w.routesLock.RLock()
  173. defer w.routesLock.RUnlock()
  174. result := make([]Route, len(w.routes))
  175. for ix := range w.routes {
  176. result[ix] = w.routes[ix]
  177. }
  178. return result
  179. }
  180. // RootPath returns the RootPath associated with this WebService. Default "/"
  181. func (w *WebService) RootPath() string {
  182. return w.rootPath
  183. }
  184. // PathParameters return the path parameter names for (shared amoung its Routes)
  185. func (w *WebService) PathParameters() []*Parameter {
  186. return w.pathParameters
  187. }
  188. // Filter adds a filter function to the chain of filters applicable to all its Routes
  189. func (w *WebService) Filter(filter FilterFunction) *WebService {
  190. w.filters = append(w.filters, filter)
  191. return w
  192. }
  193. // Doc is used to set the documentation of this service.
  194. func (w *WebService) Doc(plainText string) *WebService {
  195. w.documentation = plainText
  196. return w
  197. }
  198. // Documentation returns it.
  199. func (w *WebService) Documentation() string {
  200. return w.documentation
  201. }
  202. /*
  203. Convenience methods
  204. */
  205. // HEAD is a shortcut for .Method("HEAD").Path(subPath)
  206. func (w *WebService) HEAD(subPath string) *RouteBuilder {
  207. return new(RouteBuilder).servicePath(w.rootPath).Method("HEAD").Path(subPath)
  208. }
  209. // GET is a shortcut for .Method("GET").Path(subPath)
  210. func (w *WebService) GET(subPath string) *RouteBuilder {
  211. return new(RouteBuilder).servicePath(w.rootPath).Method("GET").Path(subPath)
  212. }
  213. // POST is a shortcut for .Method("POST").Path(subPath)
  214. func (w *WebService) POST(subPath string) *RouteBuilder {
  215. return new(RouteBuilder).servicePath(w.rootPath).Method("POST").Path(subPath)
  216. }
  217. // PUT is a shortcut for .Method("PUT").Path(subPath)
  218. func (w *WebService) PUT(subPath string) *RouteBuilder {
  219. return new(RouteBuilder).servicePath(w.rootPath).Method("PUT").Path(subPath)
  220. }
  221. // PATCH is a shortcut for .Method("PATCH").Path(subPath)
  222. func (w *WebService) PATCH(subPath string) *RouteBuilder {
  223. return new(RouteBuilder).servicePath(w.rootPath).Method("PATCH").Path(subPath)
  224. }
  225. // DELETE is a shortcut for .Method("DELETE").Path(subPath)
  226. func (w *WebService) DELETE(subPath string) *RouteBuilder {
  227. return new(RouteBuilder).servicePath(w.rootPath).Method("DELETE").Path(subPath)
  228. }