github.com/kaisawind/go-swagger@v0.19.0/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    -o, --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        -T, --template-dir=                                                         alternative template override directory
    37        -C, --config-file=                                                          configuration file to use for overriding template options
    38        -r, --copyright-file=                                                       copyright file used to add copyright header
    39            --existing-models=                                                      use pre-generated models e.g. github.com/foobar/model
    40            --additional-initialism=                                                consecutive capitals that should be considered intialisms
    41            --with-expand                                                           expands all $ref's in spec prior to generation (shorthand to --with-flatten=expand)
    42            --with-flatten=[minimal|full|expand|verbose|noverbose|remove-unused]    flattens all $ref's in spec prior to generation (default: minimal, verbose)
    43        -A, --name=                                                                 the name of the application, defaults to a mangled value of info.title
    44        -O, --operation=                                                            specify an operation to include, repeat for multiple
    45            --tags=                                                                 the tags to include, if not specified defaults to all
    46        -P, --principal=                                                            the model to use for the security principal
    47            --default-scheme=                                                       the default scheme for this API (default: http)
    48        -M, --model=                                                                specify a model to include, repeat for multiple
    49            --skip-models                                                           no models will be generated when this flag is specified
    50            --skip-operations                                                       no operations will be generated when this flag is specified
    51            --skip-support                                                          no supporting files will be generated when this flag is specified
    52            --exclude-main                                                          exclude main function, so just generate the library
    53            --exclude-spec                                                          don't embed the swagger specification
    54            --with-context                                                          handlers get a context as first arg (deprecated)
    55            --dump-data                                                             when present dumps the json for the template generator instead of generating files
    56            --flag-strategy=[go-flags|pflag]                                        the strategy to provide flags for the server (default: go-flags)
    57            --compatibility-mode=[modern|intermediate]                              the compatibility mode for the tls server (default: modern)
    58            --skip-validation                                                       skips validation of spec prior to generation
    59  ```
    60  
    61  ### Build a server
    62  
    63  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.
    64  
    65  The generated server allows for a number of command line parameters to customize it.
    66  
    67  ```
    68  --host=            the IP to listen on (default: localhost) [$HOST]
    69  --port=            the port to listen on for insecure connections, defaults to a random value [$PORT]
    70  --tls-host=        the IP to listen on for tls, when not specified it's the same as --host [$TLS_HOST]
    71  --tls-port=        the port to listen on for secure connections, defaults to a random value [$TLS_PORT]
    72  --tls-certificate= the certificate to use for secure connections [$TLS_CERTIFICATE]
    73  --tls-key=         the private key to use for secure connections [$TLS_PRIVATE_KEY]
    74  ```
    75  
    76  The server takes care of a number of things when a request arrives:
    77  
    78  * routing
    79  * authentication
    80  * input validation
    81  * content negotiation
    82  * parameter and body binding
    83  
    84  To illustrate this with a pseudo handler, this is what happens in a request.
    85  
    86  ```go
    87  import (
    88    "net/http"
    89  
    90    "github.com/go-openapi/errors"
    91    "github.com/go-openapi/runtime/middleware"
    92    "github.com/gorilla/context"
    93  )
    94  
    95  func newCompleteMiddleware(ctx *middleware.Context) http.Handler {
    96    return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
    97      defer context.Clear(r)
    98  
    99      // use context to lookup routes
   100      if matched, ok := ctx.RouteInfo(r); ok {
   101  
   102        if len(matched.Authenticators) > 0 {
   103          if _, err := ctx.Authorize(r, matched); err != nil {
   104            ctx.Respond(rw, r, matched.Produces, matched, err)
   105            return
   106          }
   107        }
   108  
   109        bound, validation := ctx.BindAndValidate(r, matched)
   110        if validation != nil {
   111          ctx.Respond(rw, r, matched.Produces, matched, validation)
   112          return
   113        }
   114  
   115        result, err := matched.Handler.Handle(bound)
   116        if err != nil {
   117          ctx.Respond(rw, r, matched.Produces, matched, err)
   118          return
   119        }
   120  
   121        ctx.Respond(rw, r, matched.Produces, matched, result)
   122        return
   123      }
   124  
   125      // Not found, check if it exists in the other methods first
   126      if others := ctx.AllowedMethods(r); len(others) > 0 {
   127        ctx.Respond(rw, r, ctx.spec.RequiredProduces(), nil, errors.MethodNotAllowed(r.Method, others))
   128        return
   129      }
   130      ctx.Respond(rw, r, ctx.spec.RequiredProduces(), nil, errors.NotFound("path %s was not found", r.URL.Path))
   131    })
   132  }
   133  ```
   134  
   135  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.
   136  
   137  For the todolist application that file looks like:
   138  
   139  ```go
   140  package main
   141  
   142  import (
   143  	"github.com/go-openapi/errors"
   144  	"github.com/go-openapi/runtime"
   145  	"github.com/go-openapi/runtime/middleware"
   146  
   147  	"github.com/go-openapi/examples/todo-list/restapi/operations"
   148  	"github.com/go-openapi/examples/todo-list/restapi/operations/todos"
   149  )
   150  
   151  // This file is safe to edit. Once it exists it will not be overwritten
   152  
   153  func configureAPI(api *operations.ToDoListAPI) http.Handler {
   154  	// configure the api here
   155  	api.JSONConsumer = runtime.JSONConsumer()
   156  
   157  	api.JSONProducer = runtime.JSONProducer()
   158  
   159  	api.KeyAuth = func(token string) (interface{}, error) {
   160  		return nil, errors.NotImplemented("api key auth (key) x-petstore-token from header has not yet been implemented")
   161  	}
   162  
   163  	api.AddOneHandler = todos.AddOneHandlerFunc(func(params todos.AddOneParams, principal interface{}) middleware.Responder {
   164  		return middleware.NotImplemented("operation addOne has not yet been implemented")
   165  	})
   166  	api.DestroyOneHandler = todos.DestroyOneHandlerFunc(func(params todos.DestroyOneParams, principal interface{}) middleware.Responder {
   167  		return middleware.NotImplemented("operation destroyOne has not yet been implemented")
   168  	})
   169  	api.FindHandler = todos.FindHandlerFunc(func(params todos.FindParams, principal interface{}) middleware.Responder {
   170  		return middleware.NotImplemented("operation find has not yet been implemented")
   171  	})
   172  	api.UpdateOneHandler = todos.UpdateOneHandlerFunc(func(params todos.UpdateOneParams, principal interface{}) middleware.Responder {
   173  		return middleware.NotImplemented("operation updateOne has not yet been implemented")
   174  	})
   175  
   176  	return setupGlobalMiddleware(api.Serve(setupMiddlewares))
   177  }
   178  ```
   179  
   180  When you look at the code for the configureAPI method then you'll notice that the api object has properties for consumers.
   181  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.
   182  
   183  The interface definitions for consumers and producers look like this:
   184  
   185  ```go
   186  // ConsumerFunc represents a function that can be used as a consumer
   187  type ConsumerFunc func(io.Reader, interface{}) error
   188  
   189  // Consume consumes the reader into the data parameter
   190  func (fn ConsumerFunc) Consume(reader io.Reader, data interface{}) error {
   191  	return fn(reader, data)
   192  }
   193  
   194  // Consumer implementations know how to bind the values on the provided interface to
   195  // data provided by the request body
   196  type Consumer interface {
   197  	// Consume performs the binding of request values
   198  	Consume(io.Reader, interface{}) error
   199  }
   200  
   201  // ProducerFunc represents a function that can be used as a producer
   202  type ProducerFunc func(io.Writer, interface{}) error
   203  
   204  // Produce produces the response for the provided data
   205  func (f ProducerFunc) Produce(writer io.Writer, data interface{}) error {
   206  	return f(writer, data)
   207  }
   208  
   209  // Producer implementations know how to turn the provided interface into a valid
   210  // HTTP response
   211  type Producer interface {
   212  	// Produce writes to the http response
   213  	Produce(io.Writer, interface{}) error
   214  }
   215  ```
   216  
   217  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.
   218  
   219  Go swagger automatically provides consumers and producers for known media types. To register a new mapping for a media
   220  type or to override an existing mapping, call the corresponding API functions in your configure_xxx.go file:
   221  
   222  ```go
   223  func configureAPI(api *operations.ToDoListAPI) http.Handler {
   224  	// other setup code here...
   225  	
   226  	api.RegisterConsumer("application/pkcs10", myCustomConsumer)
   227  	api.RegisterProducer("application/pkcs10", myCustomProducer)
   228  }
   229  
   230  ``` 
   231  
   232  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.
   233  
   234  ```go
   235  // UserPassAuthentication authentication function
   236  type UserPassAuthentication func(string, string) (interface{}, error)
   237  
   238  // TokenAuthentication authentication function
   239  type TokenAuthentication func(string) (interface{}, error)
   240  
   241  // AuthenticatorFunc turns a function into an authenticator
   242  type AuthenticatorFunc func(interface{}) (bool, interface{}, error)
   243  
   244  // Authenticate authenticates the request with the provided data
   245  func (f AuthenticatorFunc) Authenticate(params interface{}) (bool, interface{}, error) {
   246  	return f(params)
   247  }
   248  
   249  // Authenticator represents an authentication strategy
   250  // implementations of Authenticator know how to authenticate the
   251  // request data and translate that into a valid principal object or an error
   252  type Authenticator interface {
   253  	Authenticate(interface{}) (bool, interface{}, error)
   254  }
   255  ```
   256  
   257  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.
   258  
   259  ```go
   260  // AddOneHandlerFunc turns a function with the right signature into a add one handler
   261  type AddOneHandlerFunc func(AddOneParams, interface{}) middleware.Responder
   262  
   263  // Handle executing the request and returning a response
   264  func (fn AddOneHandlerFunc) Handle(params AddOneParams, principal interface{}) middleware.Responder {
   265  	return fn(params, principal)
   266  }
   267  
   268  // AddOneHandler interface for that can handle valid add one params
   269  type AddOneHandler interface {
   270  	Handle(AddOneParams, interface{}) middleware.Responder
   271  }
   272  ```
   273  
   274  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, ...
   275  
   276  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.
   277  
   278  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:
   279  
   280  ```go
   281  // NewAddOne creates a new http.Handler for the add one operation
   282  func NewAddOne(ctx *middleware.Context, handler AddOneHandler) *AddOne {
   283  	return &AddOne{Context: ctx, Handler: handler}
   284  }
   285  
   286  /*AddOne swagger:route POST / todos addOne
   287  
   288  AddOne add one API
   289  
   290  */
   291  type AddOne struct {
   292  	Context *middleware.Context
   293  	Params  AddOneParams
   294  	Handler AddOneHandler
   295  }
   296  
   297  func (o *AddOne) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
   298  	route, _ := o.Context.RouteInfo(r)
   299  
   300  	uprinc, err := o.Context.Authorize(r, route)
   301  	if err != nil {
   302  		o.Context.Respond(rw, r, route.Produces, route, err)
   303  		return
   304  	}
   305  	var principal interface{}
   306  	if uprinc != nil {
   307  		principal = uprinc
   308  	}
   309  
   310  	if err := o.Context.BindValidRequest(r, route, &o.Params); err != nil { // bind params
   311  		o.Context.Respond(rw, r, route.Produces, route, err)
   312  		return
   313  	}
   314  
   315  	res := o.Handler.Handle(o.Params, principal) // actually handle the request
   316  
   317  	o.Context.Respond(rw, r, route.Produces, route, res)
   318  
   319  }
   320  ```
   321  
   322  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:
   323  
   324  ```go
   325  // RequestBinder is an interface for types to implement
   326  // when they want to be able to bind from a request
   327  type RequestBinder interface {
   328  	BindRequest(*http.Request, *MatchedRoute) error
   329  }
   330  
   331  // AddOneParams contains all the bound params for the add one operation
   332  // typically these are obtained from a http.Request
   333  //
   334  // swagger:parameters addOne
   335  type AddOneParams struct {
   336  	/*
   337  	  In: body
   338  	*/
   339  	Body *models.Item
   340  }
   341  
   342  // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
   343  // for simple values it will use straight method calls
   344  func (o *AddOneParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
   345  	var res []error
   346  
   347  	var body models.Item
   348  	if err := route.Consumer.Consume(r.Body, &body); err != nil {
   349  		res = append(res, errors.NewParseError("body", "body", "", err))
   350  	} else {
   351  		if err := body.Validate(route.Formats); err != nil {
   352  			res = append(res, err)
   353  		}
   354  
   355  		if len(res) == 0 {
   356  			o.Body = &body
   357  		}
   358  	}
   359  
   360  	if len(res) > 0 {
   361  		return errors.CompositeValidationError(res...)
   362  	}
   363  	return nil
   364  }
   365  ```
   366  
   367  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.
   368  
   369  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).
   370  
   371  ```go
   372  // Responder is an interface for types to implement
   373  // when they want to be considered for writing HTTP responses
   374  type Responder interface {
   375  	WriteResponse(http.ResponseWriter, runtime.Producer)
   376  }
   377  
   378  /*AddOneCreated Created
   379  
   380  swagger:response addOneCreated
   381  */
   382  type AddOneCreated struct {
   383  
   384  	// In: body
   385  	Payload *models.Item `json:"body,omitempty"`
   386  }
   387  
   388  // WriteResponse to the client
   389  func (o *AddOneCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
   390  
   391  	rw.WriteHeader(201)
   392  	if o.Payload != nil {
   393  		if err := producer.Produce(rw, o.Payload); err != nil {
   394  			panic(err) // let the recovery middleware deal with this
   395  		}
   396  	}
   397  }
   398  
   399  /*AddOneDefault error
   400  
   401  swagger:response addOneDefault
   402  */
   403  type AddOneDefault struct {
   404  
   405  	// In: body
   406  	Payload *models.Error `json:"body,omitempty"`
   407  }
   408  
   409  // WriteResponse to the client
   410  func (o *AddOneDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
   411  
   412  	rw.WriteHeader(500)
   413  	if o.Payload != nil {
   414  		if err := producer.Produce(rw, o.Payload); err != nil {
   415  			panic(err) // let the recovery middleware deal with this
   416  		}
   417  	}
   418  }
   419  ```
   420  
   421  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.
   422  
   423  So to implement the AddOneHandler you could do something like this.
   424  
   425  ```go
   426  todos.AddOneHandlerFunc(func(params todos.AddOneParams, principal interface{}) middleware.Responder {
   427    created, err := database.Save(params.Body)
   428    if err != nil {
   429      return AddOneDefault{models.Error{500, err.Error()}}
   430    }
   431    return AddOneCreated{created}
   432  })
   433  ```