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