github.com/emreu/go-swagger@v0.22.1/docs/generate/server.md (about)

     1  # Generate a server from a swagger spec
     2  
     3  The toolkit has a command that will let you generate a docker friendly server with support for TLS.
     4  You can configure it through environment variables that are commonly used on PaaS services.
     5  
     6  <!--more-->
     7  
     8  A generated server uses _no reflection_ except for an enum validation and the required validation. The server builds all the necessary plans and execution paths at startup time so that at runtime there is only the absolute minimum processing required to respond to requests.
     9  
    10  The default router for go-swagger is [naoina's denco](https://github.com/naoina/denco) which is a [**very** fast](https://github.com/julienschmidt/go-http-routing-benchmark#github) ternary search tree based router that allows for much greater flexibility than the trie based router implementation of julienschmidt at almost the same and sometimes lower cost.
    11  
    12  You can provide your own router implementation should you so desire it's abstracted through an interface with this use case in mind.
    13  
    14  ### Server usage
    15  
    16  ```
    17  Usage:
    18    swagger [OPTIONS] generate server [server-OPTIONS]
    19  
    20  generate all the files for a server application
    21  
    22  Application Options:
    23    -q, --quiet                                                                     silence logs
    24        --log-output=LOG-FILE                                                       redirect logs to file
    25  
    26  Help Options:
    27    -h, --help                                                                      Show this help message
    28  
    29  [server command options]
    30        -f, --spec=                                                                 the spec file to use (default swagger.{json,yml,yaml})
    31        -a, --api-package=                                                          the package to save the operations (default: operations)
    32        -m, --model-package=                                                        the package to save the models (default: models)
    33        -s, --server-package=                                                       the package to save the server specific code (default: restapi)
    34        -c, --client-package=                                                       the package to save the client specific code (default: client)
    35        -t, --target=                                                               the base directory for generating the files (default: ./)
    36            --template=[stratoscale]                                                Load contributed templates
    37        -T, --template-dir=                                                         alternative template override directory
    38            --allow-template-override                                               allows overriding protected templates
    39        -C, --config-file=                                                          configuration file to use for overriding template options
    40        -r, --copyright-file=                                                       copyright file used to add copyright header
    41            --existing-models=                                                      use pre-generated models e.g. github.com/foobar/model
    42            --additional-initialism=                                                consecutive capitals that should be considered intialisms
    43            --with-expand                                                           expands all $ref's in spec prior to generation (shorthand to --with-flatten=expand)
    44            --with-flatten=[minimal|full|expand|verbose|noverbose|remove-unused]    flattens all $ref's in spec prior to generation (default: minimal, verbose)
    45        -A, --name=                                                                 the name of the application, defaults to a mangled value of info.title
    46        -O, --operation=                                                            specify an operation to include, repeat for multiple
    47            --tags=                                                                 the tags to include, if not specified defaults to all
    48        -P, --principal=                                                            the model to use for the security principal
    49            --default-scheme=                                                       the default scheme for this API (default: http)
    50        -M, --model=                                                                specify a model to include, repeat for multiple
    51            --skip-models                                                           no models will be generated when this flag is specified
    52            --skip-operations                                                       no operations will be generated when this flag is specified
    53            --skip-support                                                          no supporting files will be generated when this flag is specified
    54            --exclude-main                                                          exclude main function, so just generate the library
    55            --exclude-spec                                                          don't embed the swagger specification
    56            --with-context                                                          handlers get a context as first arg (deprecated)
    57            --dump-data                                                             when present dumps the json for the template generator instead of generating files
    58            --flag-strategy=[go-flags|pflag|flag]                                   the strategy to provide flags for the server (default: go-flags)
    59            --compatibility-mode=[modern|intermediate]                              the compatibility mode for the tls server (default: modern)
    60            --skip-validation                                                       skips validation of spec prior to generation
    61            --regenerate-configureapi                                               Force regeneration of configureapi.go
    62            --keep-spec-order                                                       Keep schema properties order identical to spec file
    63            --strict-additional-properties                                          disallow extra properties when additionalProperties is set to false
    64  ```
    65  
    66  ### Build a server
    67  
    68  The server application gets generated with all the handlers stubbed out with a not implemented handler. That means that you can start the API server immediately after generating it. It will respond to all valid requests with 501 Not Implemented. When a request is invalid it will most likely respond with an appropriate 4xx response.
    69  
    70  The generated server allows for a number of command line parameters to customize it.
    71  
    72  ```
    73  --host=            the IP to listen on (default: localhost) [$HOST]
    74  --port=            the port to listen on for insecure connections, defaults to a random value [$PORT]
    75  --tls-host=        the IP to listen on for tls, when not specified it's the same as --host [$TLS_HOST]
    76  --tls-port=        the port to listen on for secure connections, defaults to a random value [$TLS_PORT]
    77  --tls-certificate= the certificate to use for secure connections [$TLS_CERTIFICATE]
    78  --tls-key=         the private key to use for secure connections [$TLS_PRIVATE_KEY]
    79  ```
    80  
    81  The server takes care of a number of things when a request arrives:
    82  
    83  * routing
    84  * authentication
    85  * input validation
    86  * content negotiation
    87  * parameter and body binding
    88  
    89  To illustrate this with a pseudo handler, this is what happens in a request.
    90  
    91  ```go
    92  import (
    93    "net/http"
    94  
    95    "github.com/go-openapi/errors"
    96    "github.com/go-openapi/runtime/middleware"
    97    "github.com/gorilla/context"
    98  )
    99  
   100  func newCompleteMiddleware(ctx *middleware.Context) http.Handler {
   101    return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
   102      defer context.Clear(r)
   103  
   104      // use context to lookup routes
   105      if matched, ok := ctx.RouteInfo(r); ok {
   106  
   107        if len(matched.Authenticators) > 0 {
   108          if _, err := ctx.Authorize(r, matched); err != nil {
   109            ctx.Respond(rw, r, matched.Produces, matched, err)
   110            return
   111          }
   112        }
   113  
   114        bound, validation := ctx.BindAndValidate(r, matched)
   115        if validation != nil {
   116          ctx.Respond(rw, r, matched.Produces, matched, validation)
   117          return
   118        }
   119  
   120        result, err := matched.Handler.Handle(bound)
   121        if err != nil {
   122          ctx.Respond(rw, r, matched.Produces, matched, err)
   123          return
   124        }
   125  
   126        ctx.Respond(rw, r, matched.Produces, matched, result)
   127        return
   128      }
   129  
   130      // Not found, check if it exists in the other methods first
   131      if others := ctx.AllowedMethods(r); len(others) > 0 {
   132        ctx.Respond(rw, r, ctx.spec.RequiredProduces(), nil, errors.MethodNotAllowed(r.Method, others))
   133        return
   134      }
   135      ctx.Respond(rw, r, ctx.spec.RequiredProduces(), nil, errors.NotFound("path %s was not found", r.URL.Path))
   136    })
   137  }
   138  ```
   139  
   140  Prior to handling requests however you probably want to configure the API with some actual implementations.  To do that you have to edit the configure_xxx.go file.  That file will only be generated the first time you generate a server application from a swagger spec. So the generated server uses this file to let you fill in the blanks.
   141  
   142  For the todolist application that file looks like:
   143  
   144  ```go
   145  package main
   146  
   147  import (
   148  	"github.com/go-openapi/errors"
   149  	"github.com/go-openapi/runtime"
   150  	"github.com/go-openapi/runtime/middleware"
   151  
   152  	"github.com/go-openapi/examples/todo-list/restapi/operations"
   153  	"github.com/go-openapi/examples/todo-list/restapi/operations/todos"
   154  )
   155  
   156  // This file is safe to edit. Once it exists it will not be overwritten
   157  
   158  func configureAPI(api *operations.ToDoListAPI) http.Handler {
   159  	// configure the api here
   160  	api.JSONConsumer = runtime.JSONConsumer()
   161  
   162  	api.JSONProducer = runtime.JSONProducer()
   163  
   164  	api.KeyAuth = func(token string) (interface{}, error) {
   165  		return nil, errors.NotImplemented("api key auth (key) x-petstore-token from header has not yet been implemented")
   166  	}
   167  
   168  	api.AddOneHandler = todos.AddOneHandlerFunc(func(params todos.AddOneParams, principal interface{}) middleware.Responder {
   169  		return middleware.NotImplemented("operation addOne has not yet been implemented")
   170  	})
   171  	api.DestroyOneHandler = todos.DestroyOneHandlerFunc(func(params todos.DestroyOneParams, principal interface{}) middleware.Responder {
   172  		return middleware.NotImplemented("operation destroyOne has not yet been implemented")
   173  	})
   174  	api.FindHandler = todos.FindHandlerFunc(func(params todos.FindParams, principal interface{}) middleware.Responder {
   175  		return middleware.NotImplemented("operation find has not yet been implemented")
   176  	})
   177  	api.UpdateOneHandler = todos.UpdateOneHandlerFunc(func(params todos.UpdateOneParams, principal interface{}) middleware.Responder {
   178  		return middleware.NotImplemented("operation updateOne has not yet been implemented")
   179  	})
   180  
   181  	return setupGlobalMiddleware(api.Serve(setupMiddlewares))
   182  }
   183  ```
   184  
   185  When you look at the code for the configureAPI method then you'll notice that the api object has properties for consumers.
   186  A consumer is an object that can marshal things from a wireformat to an object.  Consumers and their counterpart producers who write objects get their names generated from the consumes and produces properties on a swagger specification.
   187  
   188  The interface definitions for consumers and producers look like this:
   189  
   190  ```go
   191  // ConsumerFunc represents a function that can be used as a consumer
   192  type ConsumerFunc func(io.Reader, interface{}) error
   193  
   194  // Consume consumes the reader into the data parameter
   195  func (fn ConsumerFunc) Consume(reader io.Reader, data interface{}) error {
   196  	return fn(reader, data)
   197  }
   198  
   199  // Consumer implementations know how to bind the values on the provided interface to
   200  // data provided by the request body
   201  type Consumer interface {
   202  	// Consume performs the binding of request values
   203  	Consume(io.Reader, interface{}) error
   204  }
   205  
   206  // ProducerFunc represents a function that can be used as a producer
   207  type ProducerFunc func(io.Writer, interface{}) error
   208  
   209  // Produce produces the response for the provided data
   210  func (f ProducerFunc) Produce(writer io.Writer, data interface{}) error {
   211  	return f(writer, data)
   212  }
   213  
   214  // Producer implementations know how to turn the provided interface into a valid
   215  // HTTP response
   216  type Producer interface {
   217  	// Produce writes to the http response
   218  	Produce(io.Writer, interface{}) error
   219  }
   220  ```
   221  
   222  So it's something that can turn a reader into a hydrated interface. A producer is the counterpart of a consumer and writes objects to an io.Writer.  When you configure an api with those you make sure it can marshal the types for the supported content types.
   223  
   224  Go swagger automatically provides consumers and producers for known media types. To register a new mapping for a media
   225  type or to override an existing mapping, call the corresponding API functions in your configure_xxx.go file:
   226  
   227  ```go
   228  func configureAPI(api *operations.ToDoListAPI) http.Handler {
   229  	// other setup code here...
   230  
   231  	api.RegisterConsumer("application/pkcs10", myCustomConsumer)
   232  	api.RegisterProducer("application/pkcs10", myCustomProducer)
   233  }
   234  
   235  ```
   236  
   237  The next thing that happens in the configureAPI method is setting up the authentication with a stub handler in this case. This particular swagger specification supports token based authentication and as such it wants you to configure a token auth handler.  Any error for an authentication handler is assumed to be an invalid authentication and will return the 401 status code.
   238  
   239  ```go
   240  // UserPassAuthentication authentication function
   241  type UserPassAuthentication func(string, string) (interface{}, error)
   242  
   243  // TokenAuthentication authentication function
   244  type TokenAuthentication func(string) (interface{}, error)
   245  
   246  // AuthenticatorFunc turns a function into an authenticator
   247  type AuthenticatorFunc func(interface{}) (bool, interface{}, error)
   248  
   249  // Authenticate authenticates the request with the provided data
   250  func (f AuthenticatorFunc) Authenticate(params interface{}) (bool, interface{}, error) {
   251  	return f(params)
   252  }
   253  
   254  // Authenticator represents an authentication strategy
   255  // implementations of Authenticator know how to authenticate the
   256  // request data and translate that into a valid principal object or an error
   257  type Authenticator interface {
   258  	Authenticate(interface{}) (bool, interface{}, error)
   259  }
   260  ```
   261  
   262  So we finally get to configuring our route handlers. For each operation there exists an interface so that implementations have some freedom to provide alternative implementations. For example mocks in certain tests, automatic stubbing handlers, not implemented handlers. Let's look at the addOne handler in a bit more detail.
   263  
   264  ```go
   265  // AddOneHandlerFunc turns a function with the right signature into a add one handler
   266  type AddOneHandlerFunc func(AddOneParams, interface{}) middleware.Responder
   267  
   268  // Handle executing the request and returning a response
   269  func (fn AddOneHandlerFunc) Handle(params AddOneParams, principal interface{}) middleware.Responder {
   270  	return fn(params, principal)
   271  }
   272  
   273  // AddOneHandler interface for that can handle valid add one params
   274  type AddOneHandler interface {
   275  	Handle(AddOneParams, interface{}) middleware.Responder
   276  }
   277  ```
   278  
   279  Because the `addOne` operation requires authentication, this interface definition requires 2 arguments. The first argument is about the request parameters and the second parameter is the security principal for the request.  In this case it is of type `interface{}`, typically that is a type like Account, User, Session, ...
   280  
   281  It is your job to provide such a handler. Go swagger guarantees that by the time the request processing ends up at the handler, the parameters and security principal have been bound and validated.  So you can safely proceed with saving the request body to some persistence medium perhaps.
   282  
   283  There is a context that gets created where the handlers get wired up into a `http.Handler`. For the add one this looks like this:
   284  
   285  ```go
   286  // NewAddOne creates a new http.Handler for the add one operation
   287  func NewAddOne(ctx *middleware.Context, handler AddOneHandler) *AddOne {
   288  	return &AddOne{Context: ctx, Handler: handler}
   289  }
   290  
   291  /*AddOne swagger:route POST / todos addOne
   292  
   293  AddOne add one API
   294  
   295  */
   296  type AddOne struct {
   297  	Context *middleware.Context
   298  	Params  AddOneParams
   299  	Handler AddOneHandler
   300  }
   301  
   302  func (o *AddOne) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
   303  	route, _ := o.Context.RouteInfo(r)
   304  
   305  	uprinc, err := o.Context.Authorize(r, route)
   306  	if err != nil {
   307  		o.Context.Respond(rw, r, route.Produces, route, err)
   308  		return
   309  	}
   310  	var principal interface{}
   311  	if uprinc != nil {
   312  		principal = uprinc
   313  	}
   314  
   315  	if err := o.Context.BindValidRequest(r, route, &o.Params); err != nil { // bind params
   316  		o.Context.Respond(rw, r, route.Produces, route, err)
   317  		return
   318  	}
   319  
   320  	res := o.Handler.Handle(o.Params, principal) // actually handle the request
   321  
   322  	o.Context.Respond(rw, r, route.Produces, route, res)
   323  
   324  }
   325  ```
   326  
   327  The `http.Handler` implementation takes care of authentication, binding, user code execution and generating a response. For authentication this request would end up in the `TokenAuthentication` handler that was put on the api context object earlier.  When a request is authenticated it gets bound. This operation eventually requires an object that is an implementation of `RequestBinder`.  The `AddOneParams` are such an implementation:
   328  
   329  ```go
   330  // RequestBinder is an interface for types to implement
   331  // when they want to be able to bind from a request
   332  type RequestBinder interface {
   333  	BindRequest(*http.Request, *MatchedRoute) error
   334  }
   335  
   336  // AddOneParams contains all the bound params for the add one operation
   337  // typically these are obtained from a http.Request
   338  //
   339  // swagger:parameters addOne
   340  type AddOneParams struct {
   341  	/*
   342  	  In: body
   343  	*/
   344  	Body *models.Item
   345  }
   346  
   347  // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
   348  // for simple values it will use straight method calls
   349  func (o *AddOneParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
   350  	var res []error
   351  
   352  	var body models.Item
   353  	if err := route.Consumer.Consume(r.Body, &body); err != nil {
   354  		res = append(res, errors.NewParseError("body", "body", "", err))
   355  	} else {
   356  		if err := body.Validate(route.Formats); err != nil {
   357  			res = append(res, err)
   358  		}
   359  
   360  		if len(res) == 0 {
   361  			o.Body = &body
   362  		}
   363  	}
   364  
   365  	if len(res) > 0 {
   366  		return errors.CompositeValidationError(res...)
   367  	}
   368  	return nil
   369  }
   370  ```
   371  
   372  In this example there is only a body parameter, so we make use of the selected consumer to read the request body and turn it into an instance of models.Item. When the body parameter is bound, it gets validated and when validation passes no error is returned and the body property is set.  After a request is bound and validated the parameters and security principal are passed to the request handler. For this configuration that would return a 501 responder.
   373  
   374  Go swagger uses responders which are an interface implementation for things that can write to a response. For the generated server there are status code response and a default response object generated for every entry in the spec. For the `addOne` operation that are 2 objects one for the success case (201) and one for an error (default).
   375  
   376  ```go
   377  // Responder is an interface for types to implement
   378  // when they want to be considered for writing HTTP responses
   379  type Responder interface {
   380  	WriteResponse(http.ResponseWriter, runtime.Producer)
   381  }
   382  
   383  /*AddOneCreated Created
   384  
   385  swagger:response addOneCreated
   386  */
   387  type AddOneCreated struct {
   388  
   389  	// In: body
   390  	Payload *models.Item `json:"body,omitempty"`
   391  }
   392  
   393  // WriteResponse to the client
   394  func (o *AddOneCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
   395  
   396  	rw.WriteHeader(201)
   397  	if o.Payload != nil {
   398  		if err := producer.Produce(rw, o.Payload); err != nil {
   399  			panic(err) // let the recovery middleware deal with this
   400  		}
   401  	}
   402  }
   403  
   404  /*AddOneDefault error
   405  
   406  swagger:response addOneDefault
   407  */
   408  type AddOneDefault struct {
   409  
   410  	// In: body
   411  	Payload *models.Error `json:"body,omitempty"`
   412  }
   413  
   414  // WriteResponse to the client
   415  func (o *AddOneDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
   416  
   417  	rw.WriteHeader(500)
   418  	if o.Payload != nil {
   419  		if err := producer.Produce(rw, o.Payload); err != nil {
   420  			panic(err) // let the recovery middleware deal with this
   421  		}
   422  	}
   423  }
   424  ```
   425  
   426  So an implementer of the `AddOneHandler` could return one of these 2 objects and go-swagger is able to respect the contract set forward by the spec document.
   427  
   428  So to implement the AddOneHandler you could do something like this.
   429  
   430  ```go
   431  todos.AddOneHandlerFunc(func(params todos.AddOneParams, principal interface{}) middleware.Responder {
   432    created, err := database.Save(params.Body)
   433    if err != nil {
   434      return AddOneDefault{models.Error{500, err.Error()}}
   435    }
   436    return AddOneCreated{created}
   437  })
   438  ```