291 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			291 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
| package restful
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"os"
 | |
| 	"reflect"
 | |
| 	"sync"
 | |
| 
 | |
| 	"github.com/emicklei/go-restful/log"
 | |
| )
 | |
| 
 | |
| // Copyright 2013 Ernest Micklei. All rights reserved.
 | |
| // Use of this source code is governed by a license
 | |
| // that can be found in the LICENSE file.
 | |
| 
 | |
| // WebService holds a collection of Route values that bind a Http Method + URL Path to a function.
 | |
| type WebService struct {
 | |
| 	rootPath       string
 | |
| 	pathExpr       *pathExpression // cached compilation of rootPath as RegExp
 | |
| 	routes         []Route
 | |
| 	produces       []string
 | |
| 	consumes       []string
 | |
| 	pathParameters []*Parameter
 | |
| 	filters        []FilterFunction
 | |
| 	documentation  string
 | |
| 	apiVersion     string
 | |
| 
 | |
| 	typeNameHandleFunc TypeNameHandleFunction
 | |
| 
 | |
| 	dynamicRoutes bool
 | |
| 
 | |
| 	// protects 'routes' if dynamic routes are enabled
 | |
| 	routesLock sync.RWMutex
 | |
| }
 | |
| 
 | |
| func (w *WebService) SetDynamicRoutes(enable bool) {
 | |
| 	w.dynamicRoutes = enable
 | |
| }
 | |
| 
 | |
| // TypeNameHandleFunction declares functions that can handle translating the name of a sample object
 | |
| // into the restful documentation for the service.
 | |
| type TypeNameHandleFunction func(sample interface{}) string
 | |
| 
 | |
| // TypeNameHandler sets the function that will convert types to strings in the parameter
 | |
| // and model definitions. If not set, the web service will invoke
 | |
| // reflect.TypeOf(object).String().
 | |
| func (w *WebService) TypeNameHandler(handler TypeNameHandleFunction) *WebService {
 | |
| 	w.typeNameHandleFunc = handler
 | |
| 	return w
 | |
| }
 | |
| 
 | |
| // reflectTypeName is the default TypeNameHandleFunction and for a given object
 | |
| // returns the name that Go identifies it with (e.g. "string" or "v1.Object") via
 | |
| // the reflection API.
 | |
| func reflectTypeName(sample interface{}) string {
 | |
| 	return reflect.TypeOf(sample).String()
 | |
| }
 | |
| 
 | |
| // compilePathExpression ensures that the path is compiled into a RegEx for those routers that need it.
 | |
| func (w *WebService) compilePathExpression() {
 | |
| 	compiled, err := newPathExpression(w.rootPath)
 | |
| 	if err != nil {
 | |
| 		log.Printf("invalid path:%s because:%v", w.rootPath, err)
 | |
| 		os.Exit(1)
 | |
| 	}
 | |
| 	w.pathExpr = compiled
 | |
| }
 | |
| 
 | |
| // ApiVersion sets the API version for documentation purposes.
 | |
| func (w *WebService) ApiVersion(apiVersion string) *WebService {
 | |
| 	w.apiVersion = apiVersion
 | |
| 	return w
 | |
| }
 | |
| 
 | |
| // Version returns the API version for documentation purposes.
 | |
| func (w *WebService) Version() string { return w.apiVersion }
 | |
| 
 | |
| // Path specifies the root URL template path of the WebService.
 | |
| // All Routes will be relative to this path.
 | |
| func (w *WebService) Path(root string) *WebService {
 | |
| 	w.rootPath = root
 | |
| 	if len(w.rootPath) == 0 {
 | |
| 		w.rootPath = "/"
 | |
| 	}
 | |
| 	w.compilePathExpression()
 | |
| 	return w
 | |
| }
 | |
| 
 | |
| // Param adds a PathParameter to document parameters used in the root path.
 | |
| func (w *WebService) Param(parameter *Parameter) *WebService {
 | |
| 	if w.pathParameters == nil {
 | |
| 		w.pathParameters = []*Parameter{}
 | |
| 	}
 | |
| 	w.pathParameters = append(w.pathParameters, parameter)
 | |
| 	return w
 | |
| }
 | |
| 
 | |
| // PathParameter creates a new Parameter of kind Path for documentation purposes.
 | |
| // It is initialized as required with string as its DataType.
 | |
| func (w *WebService) PathParameter(name, description string) *Parameter {
 | |
| 	return PathParameter(name, description)
 | |
| }
 | |
| 
 | |
| // PathParameter creates a new Parameter of kind Path for documentation purposes.
 | |
| // It is initialized as required with string as its DataType.
 | |
| func PathParameter(name, description string) *Parameter {
 | |
| 	p := &Parameter{&ParameterData{Name: name, Description: description, Required: true, DataType: "string"}}
 | |
| 	p.bePath()
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| // QueryParameter creates a new Parameter of kind Query for documentation purposes.
 | |
| // It is initialized as not required with string as its DataType.
 | |
| func (w *WebService) QueryParameter(name, description string) *Parameter {
 | |
| 	return QueryParameter(name, description)
 | |
| }
 | |
| 
 | |
| // QueryParameter creates a new Parameter of kind Query for documentation purposes.
 | |
| // It is initialized as not required with string as its DataType.
 | |
| func QueryParameter(name, description string) *Parameter {
 | |
| 	p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string", CollectionFormat: CollectionFormatCSV.String()}}
 | |
| 	p.beQuery()
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| // BodyParameter creates a new Parameter of kind Body for documentation purposes.
 | |
| // It is initialized as required without a DataType.
 | |
| func (w *WebService) BodyParameter(name, description string) *Parameter {
 | |
| 	return BodyParameter(name, description)
 | |
| }
 | |
| 
 | |
| // BodyParameter creates a new Parameter of kind Body for documentation purposes.
 | |
| // It is initialized as required without a DataType.
 | |
| func BodyParameter(name, description string) *Parameter {
 | |
| 	p := &Parameter{&ParameterData{Name: name, Description: description, Required: true}}
 | |
| 	p.beBody()
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| // HeaderParameter creates a new Parameter of kind (Http) Header for documentation purposes.
 | |
| // It is initialized as not required with string as its DataType.
 | |
| func (w *WebService) HeaderParameter(name, description string) *Parameter {
 | |
| 	return HeaderParameter(name, description)
 | |
| }
 | |
| 
 | |
| // HeaderParameter creates a new Parameter of kind (Http) Header for documentation purposes.
 | |
| // It is initialized as not required with string as its DataType.
 | |
| func HeaderParameter(name, description string) *Parameter {
 | |
| 	p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}}
 | |
| 	p.beHeader()
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| // FormParameter creates a new Parameter of kind Form (using application/x-www-form-urlencoded) for documentation purposes.
 | |
| // It is initialized as required with string as its DataType.
 | |
| func (w *WebService) FormParameter(name, description string) *Parameter {
 | |
| 	return FormParameter(name, description)
 | |
| }
 | |
| 
 | |
| // FormParameter creates a new Parameter of kind Form (using application/x-www-form-urlencoded) for documentation purposes.
 | |
| // It is initialized as required with string as its DataType.
 | |
| func FormParameter(name, description string) *Parameter {
 | |
| 	p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}}
 | |
| 	p.beForm()
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| // Route creates a new Route using the RouteBuilder and add to the ordered list of Routes.
 | |
| func (w *WebService) Route(builder *RouteBuilder) *WebService {
 | |
| 	w.routesLock.Lock()
 | |
| 	defer w.routesLock.Unlock()
 | |
| 	builder.copyDefaults(w.produces, w.consumes)
 | |
| 	w.routes = append(w.routes, builder.Build())
 | |
| 	return w
 | |
| }
 | |
| 
 | |
| // RemoveRoute removes the specified route, looks for something that matches 'path' and 'method'
 | |
| func (w *WebService) RemoveRoute(path, method string) error {
 | |
| 	if !w.dynamicRoutes {
 | |
| 		return errors.New("dynamic routes are not enabled.")
 | |
| 	}
 | |
| 	w.routesLock.Lock()
 | |
| 	defer w.routesLock.Unlock()
 | |
| 	newRoutes := make([]Route, (len(w.routes) - 1))
 | |
| 	current := 0
 | |
| 	for ix := range w.routes {
 | |
| 		if w.routes[ix].Method == method && w.routes[ix].Path == path {
 | |
| 			continue
 | |
| 		}
 | |
| 		newRoutes[current] = w.routes[ix]
 | |
| 		current = current + 1
 | |
| 	}
 | |
| 	w.routes = newRoutes
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Method creates a new RouteBuilder and initialize its http method
 | |
| func (w *WebService) Method(httpMethod string) *RouteBuilder {
 | |
| 	return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method(httpMethod)
 | |
| }
 | |
| 
 | |
| // Produces specifies that this WebService can produce one or more MIME types.
 | |
| // Http requests must have one of these values set for the Accept header.
 | |
| func (w *WebService) Produces(contentTypes ...string) *WebService {
 | |
| 	w.produces = contentTypes
 | |
| 	return w
 | |
| }
 | |
| 
 | |
| // Consumes specifies that this WebService can consume one or more MIME types.
 | |
| // Http requests must have one of these values set for the Content-Type header.
 | |
| func (w *WebService) Consumes(accepts ...string) *WebService {
 | |
| 	w.consumes = accepts
 | |
| 	return w
 | |
| }
 | |
| 
 | |
| // Routes returns the Routes associated with this WebService
 | |
| func (w *WebService) Routes() []Route {
 | |
| 	if !w.dynamicRoutes {
 | |
| 		return w.routes
 | |
| 	}
 | |
| 	// Make a copy of the array to prevent concurrency problems
 | |
| 	w.routesLock.RLock()
 | |
| 	defer w.routesLock.RUnlock()
 | |
| 	result := make([]Route, len(w.routes))
 | |
| 	for ix := range w.routes {
 | |
| 		result[ix] = w.routes[ix]
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| // RootPath returns the RootPath associated with this WebService. Default "/"
 | |
| func (w *WebService) RootPath() string {
 | |
| 	return w.rootPath
 | |
| }
 | |
| 
 | |
| // PathParameters return the path parameter names for (shared among its Routes)
 | |
| func (w *WebService) PathParameters() []*Parameter {
 | |
| 	return w.pathParameters
 | |
| }
 | |
| 
 | |
| // Filter adds a filter function to the chain of filters applicable to all its Routes
 | |
| func (w *WebService) Filter(filter FilterFunction) *WebService {
 | |
| 	w.filters = append(w.filters, filter)
 | |
| 	return w
 | |
| }
 | |
| 
 | |
| // Doc is used to set the documentation of this service.
 | |
| func (w *WebService) Doc(plainText string) *WebService {
 | |
| 	w.documentation = plainText
 | |
| 	return w
 | |
| }
 | |
| 
 | |
| // Documentation returns it.
 | |
| func (w *WebService) Documentation() string {
 | |
| 	return w.documentation
 | |
| }
 | |
| 
 | |
| /*
 | |
| 	Convenience methods
 | |
| */
 | |
| 
 | |
| // HEAD is a shortcut for .Method("HEAD").Path(subPath)
 | |
| func (w *WebService) HEAD(subPath string) *RouteBuilder {
 | |
| 	return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("HEAD").Path(subPath)
 | |
| }
 | |
| 
 | |
| // GET is a shortcut for .Method("GET").Path(subPath)
 | |
| func (w *WebService) GET(subPath string) *RouteBuilder {
 | |
| 	return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("GET").Path(subPath)
 | |
| }
 | |
| 
 | |
| // POST is a shortcut for .Method("POST").Path(subPath)
 | |
| func (w *WebService) POST(subPath string) *RouteBuilder {
 | |
| 	return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("POST").Path(subPath)
 | |
| }
 | |
| 
 | |
| // PUT is a shortcut for .Method("PUT").Path(subPath)
 | |
| func (w *WebService) PUT(subPath string) *RouteBuilder {
 | |
| 	return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("PUT").Path(subPath)
 | |
| }
 | |
| 
 | |
| // PATCH is a shortcut for .Method("PATCH").Path(subPath)
 | |
| func (w *WebService) PATCH(subPath string) *RouteBuilder {
 | |
| 	return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("PATCH").Path(subPath)
 | |
| }
 | |
| 
 | |
| // DELETE is a shortcut for .Method("DELETE").Path(subPath)
 | |
| func (w *WebService) DELETE(subPath string) *RouteBuilder {
 | |
| 	return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("DELETE").Path(subPath)
 | |
| }
 |