web_service.go 11 KB

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