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

     1  ---
     2  title: Generating a server
     3  date: 2023-01-01T01:01:01-08:00
     4  draft: true
     5  weight: 30
     6  ---
     7  # Generate a server from a swagger spec
     8  
     9  The toolkit has a command that will let you generate a docker friendly server with support for TLS.
    10  You can configure it through environment variables that are commonly used on PaaS services.
    11  
    12  <!--more-->
    13  
    14  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.
    15  
    16  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.
    17  
    18  You can provide your own router implementation should you so desire it's abstracted through an interface with this use case in mind.
    19  
    20  ### Server usage
    21  
    22  ```
    23  Usage:
    24    swagger [OPTIONS] generate server [server-OPTIONS]
    25  
    26  generate all the files for a server application
    27  
    28  Application Options:
    29    -q, --quiet                                                                     silence logs
    30        --log-output=LOG-FILE                                                       redirect logs to file
    31  
    32  Help Options:
    33    -h, --help                                                                      Show this help message
    34  
    35  [server command options]
    36        -s, --server-package=                                                       the package to save the server specific code (default: restapi)
    37            --main-package=                                                         the location of the generated main. Defaults to cmd/{name}-server
    38        -P, --principal=                                                            the model to use for the security principal
    39            --default-scheme=                                                       the default scheme for this API (default: http)
    40            --principal-is-interface                                                the security principal provided is an interface, not a struct
    41            --default-produces=                                                     the default mime type that API operations produce (default: application/json)
    42            --default-consumes=                                                     the default mime type that API operations consume (default: application/json)
    43            --skip-models                                                           no models will be generated when this flag is specified
    44            --skip-operations                                                       no operations will be generated when this flag is specified
    45            --skip-support                                                          no supporting files will be generated when this flag is specified
    46            --exclude-main                                                          exclude main function, so just generate the library
    47            --exclude-spec                                                          don't embed the swagger specification
    48            --flag-strategy=[go-flags|pflag|flag]                                   the strategy to provide flags for the server (default: go-flags)
    49            --compatibility-mode=[modern|intermediate]                              the compatibility mode for the tls server (default: modern)
    50            --regenerate-configureapi                                               Force regeneration of configureapi.go
    51        -A, --name=                                                                 the name of the application, defaults to a mangled value of info.title
    52            --with-context                                                          handlers get a context as first arg (deprecated)
    53  
    54      Options common to all code generation commands:
    55        -f, --spec=                                                                 the spec file to use (default swagger.{json,yml,yaml})
    56        -t, --target=                                                               the base directory for generating the files (default: ./)
    57            --template=[stratoscale]                                                load contributed templates
    58        -T, --template-dir=                                                         alternative template override directory
    59        -C, --config-file=                                                          configuration file to use for overriding template options
    60        -r, --copyright-file=                                                       copyright file used to add copyright header
    61            --additional-initialism=                                                consecutive capitals that should be considered intialisms
    62            --allow-template-override                                               allows overriding protected templates
    63            --skip-validation                                                       skips validation of spec prior to generation
    64            --dump-data                                                             when present dumps the json for the template generator instead of generating files
    65            --with-expand                                                           expands all $ref's in spec prior to generation (shorthand to --with-flatten=expand)
    66            --with-flatten=[minimal|full|expand|verbose|noverbose|remove-unused]    flattens all $ref's in spec prior to generation (default: minimal, verbose)
    67  
    68      Options for model generation:
    69        -m, --model-package=                                                        the package to save the models (default: models)
    70        -M, --model=                                                                specify a model to include in generation, repeat for multiple (defaults to all)
    71            --existing-models=                                                      use pre-generated models e.g. github.com/foobar/model
    72            --strict-additional-properties                                          disallow extra properties when additionalProperties is set to false
    73            --keep-spec-order                                                       keep schema properties order identical to spec file
    74            --struct-tags                                                           specify custom struct tags for third-party libraries, repeat for multiple (defaults to json)
    75  
    76      Options for operation generation:
    77        -O, --operation=                                                            specify an operation to include, repeat for multiple (defaults to all)
    78            --tags=                                                                 the tags to include, if not specified defaults to all
    79        -a, --api-package=                                                          the package to save the operations (default: operations)
    80            --with-enum-ci                                                          set all enumerations case-insensitive by default
    81            --skip-tag-packages                                                     skips the generation of tag-based operation packages, resulting in a flat generation
    82  ```
    83  
    84  ### Build a server
    85  
    86  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.
    87  
    88  The generated server allows for a number of command line parameters to customize it.
    89  
    90  ```
    91        --cleanup-timeout duration     grace period for which to wait before killing idle connections (default 10s)
    92        --graceful-timeout duration    grace period for which to wait before shutting down the server (default 15s)
    93        --host string                  the IP to listen on (default "localhost")
    94        --keep-alive duration          sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download) (default 3m0s)
    95        --listen-limit int             limit the number of outstanding requests
    96        --max-header-size byte-size    controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body (default 1MB)
    97        --port int                     the port to listen on for insecure connections, defaults to a random value
    98        --read-timeout duration        maximum duration before timing out read of the request (default 30s)
    99        --scheme strings               the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec (default [http,https,unix])
   100        --socket-path string           the unix socket to listen on (default "/var/run/todo-list.sock")
   101        --tls-ca string                the certificate authority certificate file to be used with mutual tls auth
   102        --tls-certificate string       the certificate file to use for secure connections
   103        --tls-host string              the IP to listen on (default "localhost")
   104        --tls-keep-alive duration      sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download) (default 3m0s)
   105        --tls-key string               the private key file to use for secure connections (without passphrase)
   106        --tls-listen-limit int         limit the number of outstanding requests
   107        --tls-port int                 the port to listen on for secure connections, defaults to a random value
   108        --tls-read-timeout duration    maximum duration before timing out read of the request (default 30s)
   109        --tls-write-timeout duration   maximum duration before timing out write of the response (default 30s)
   110        --write-timeout duration       maximum duration before timing out write of the response (default 30s)
   111  ```
   112  
   113  The server takes care of a number of things when a request arrives:
   114  
   115  * routing
   116  * authentication
   117  * input validation
   118  * content negotiation
   119  * parameter and body binding
   120  
   121  To illustrate this with a pseudo handler, this is what happens in a request.
   122  
   123  ```go
   124  import (
   125    "net/http"
   126  
   127    "github.com/go-openapi/errors"
   128    "github.com/go-openapi/runtime/middleware"
   129    "github.com/gorilla/context"
   130  )
   131  
   132  func newCompleteMiddleware(ctx *middleware.Context) http.Handler {
   133    return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
   134      defer context.Clear(r)
   135  
   136      // use context to lookup routes
   137      if matched, ok := ctx.RouteInfo(r); ok {
   138  
   139        if len(matched.Authenticators) > 0 {
   140          if _, err := ctx.Authorize(r, matched); err != nil {
   141            ctx.Respond(rw, r, matched.Produces, matched, err)
   142            return
   143          }
   144        }
   145  
   146        bound, validation := ctx.BindAndValidate(r, matched)
   147        if validation != nil {
   148          ctx.Respond(rw, r, matched.Produces, matched, validation)
   149          return
   150        }
   151  
   152        result, err := matched.Handler.Handle(bound)
   153        if err != nil {
   154          ctx.Respond(rw, r, matched.Produces, matched, err)
   155          return
   156        }
   157  
   158        ctx.Respond(rw, r, matched.Produces, matched, result)
   159        return
   160      }
   161  
   162      // Not found, check if it exists in the other methods first
   163      if others := ctx.AllowedMethods(r); len(others) > 0 {
   164        ctx.Respond(rw, r, ctx.spec.RequiredProduces(), nil, errors.MethodNotAllowed(r.Method, others))
   165        return
   166      }
   167      ctx.Respond(rw, r, ctx.spec.RequiredProduces(), nil, errors.NotFound("path %s was not found", r.URL.Path))
   168    })
   169  }
   170  ```
   171  
   172  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.
   173  
   174  For the todolist application that file looks like:
   175  
   176  ```go
   177  package main
   178  
   179  import (
   180  	"github.com/go-openapi/errors"
   181  	"github.com/go-openapi/runtime"
   182  	"github.com/go-openapi/runtime/middleware"
   183  
   184  	"github.com/go-openapi/examples/todo-list/restapi/operations"
   185  	"github.com/go-openapi/examples/todo-list/restapi/operations/todos"
   186  )
   187  
   188  // This file is safe to edit. Once it exists it will not be overwritten
   189  
   190  func configureAPI(api *operations.ToDoListAPI) http.Handler {
   191  	// configure the api here
   192  	api.JSONConsumer = runtime.JSONConsumer()
   193  
   194  	api.JSONProducer = runtime.JSONProducer()
   195  
   196  	api.KeyAuth = func(token string) (interface{}, error) {
   197  		return nil, errors.NotImplemented("api key auth (key) x-petstore-token from header has not yet been implemented")
   198  	}
   199  
   200  	api.AddOneHandler = todos.AddOneHandlerFunc(func(params todos.AddOneParams, principal interface{}) middleware.Responder {
   201  		return middleware.NotImplemented("operation addOne has not yet been implemented")
   202  	})
   203  	api.DestroyOneHandler = todos.DestroyOneHandlerFunc(func(params todos.DestroyOneParams, principal interface{}) middleware.Responder {
   204  		return middleware.NotImplemented("operation destroyOne has not yet been implemented")
   205  	})
   206  	api.FindHandler = todos.FindHandlerFunc(func(params todos.FindParams, principal interface{}) middleware.Responder {
   207  		return middleware.NotImplemented("operation find has not yet been implemented")
   208  	})
   209  	api.UpdateOneHandler = todos.UpdateOneHandlerFunc(func(params todos.UpdateOneParams, principal interface{}) middleware.Responder {
   210  		return middleware.NotImplemented("operation updateOne has not yet been implemented")
   211  	})
   212  
   213  	return setupGlobalMiddleware(api.Serve(setupMiddlewares))
   214  }
   215  ```
   216  
   217  When you look at the code for the configureAPI method then you'll notice that the api object has properties for consumers.
   218  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.
   219  
   220  Often, this will be JSON. If you want to use XML, additionally you have to enable XML compatible models when generating the server. For that, you have to set the command options `--default-consumes` or `--default-produces` to an XML mime type like `application/xml`. For more details on using XML, also see the [client generation](client.md).
   221  
   222  The interface definitions for consumers and producers look like this:
   223  
   224  ```go
   225  // ConsumerFunc represents a function that can be used as a consumer
   226  type ConsumerFunc func(io.Reader, interface{}) error
   227  
   228  // Consume consumes the reader into the data parameter
   229  func (fn ConsumerFunc) Consume(reader io.Reader, data interface{}) error {
   230  	return fn(reader, data)
   231  }
   232  
   233  // Consumer implementations know how to bind the values on the provided interface to
   234  // data provided by the request body
   235  type Consumer interface {
   236  	// Consume performs the binding of request values
   237  	Consume(io.Reader, interface{}) error
   238  }
   239  
   240  // ProducerFunc represents a function that can be used as a producer
   241  type ProducerFunc func(io.Writer, interface{}) error
   242  
   243  // Produce produces the response for the provided data
   244  func (f ProducerFunc) Produce(writer io.Writer, data interface{}) error {
   245  	return f(writer, data)
   246  }
   247  
   248  // Producer implementations know how to turn the provided interface into a valid
   249  // HTTP response
   250  type Producer interface {
   251  	// Produce writes to the http response
   252  	Produce(io.Writer, interface{}) error
   253  }
   254  ```
   255  
   256  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.
   257  
   258  Go swagger automatically provides consumers and producers for known media types. To register a new mapping for a media
   259  type or to override an existing mapping, call the corresponding API functions in your configure_xxx.go file:
   260  
   261  ```go
   262  func configureAPI(api *operations.ToDoListAPI) http.Handler {
   263  	// other setup code here...
   264  
   265  	api.RegisterConsumer("application/pkcs10", myCustomConsumer)
   266  	api.RegisterProducer("application/pkcs10", myCustomProducer)
   267  }
   268  
   269  ```
   270  
   271  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.
   272  
   273  ```go
   274  // UserPassAuthentication authentication function
   275  type UserPassAuthentication func(string, string) (interface{}, error)
   276  
   277  // TokenAuthentication authentication function
   278  type TokenAuthentication func(string) (interface{}, error)
   279  
   280  // AuthenticatorFunc turns a function into an authenticator
   281  type AuthenticatorFunc func(interface{}) (bool, interface{}, error)
   282  
   283  // Authenticate authenticates the request with the provided data
   284  func (f AuthenticatorFunc) Authenticate(params interface{}) (bool, interface{}, error) {
   285  	return f(params)
   286  }
   287  
   288  // Authenticator represents an authentication strategy
   289  // implementations of Authenticator know how to authenticate the
   290  // request data and translate that into a valid principal object or an error
   291  type Authenticator interface {
   292  	Authenticate(interface{}) (bool, interface{}, error)
   293  }
   294  ```
   295  
   296  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.
   297  
   298  ```go
   299  // AddOneHandlerFunc turns a function with the right signature into a add one handler
   300  type AddOneHandlerFunc func(AddOneParams, interface{}) middleware.Responder
   301  
   302  // Handle executing the request and returning a response
   303  func (fn AddOneHandlerFunc) Handle(params AddOneParams, principal interface{}) middleware.Responder {
   304  	return fn(params, principal)
   305  }
   306  
   307  // AddOneHandler interface for that can handle valid add one params
   308  type AddOneHandler interface {
   309  	Handle(AddOneParams, interface{}) middleware.Responder
   310  }
   311  ```
   312  
   313  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, ...
   314  
   315  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.
   316  
   317  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:
   318  
   319  ```go
   320  // NewAddOne creates a new http.Handler for the add one operation
   321  func NewAddOne(ctx *middleware.Context, handler AddOneHandler) *AddOne {
   322  	return &AddOne{Context: ctx, Handler: handler}
   323  }
   324  
   325  /*AddOne swagger:route POST / todos addOne
   326  
   327  AddOne add one API
   328  
   329  */
   330  type AddOne struct {
   331  	Context *middleware.Context
   332  	Params  AddOneParams
   333  	Handler AddOneHandler
   334  }
   335  
   336  func (o *AddOne) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
   337  	route, _ := o.Context.RouteInfo(r)
   338  
   339  	uprinc, err := o.Context.Authorize(r, route)
   340  	if err != nil {
   341  		o.Context.Respond(rw, r, route.Produces, route, err)
   342  		return
   343  	}
   344  	var principal interface{}
   345  	if uprinc != nil {
   346  		principal = uprinc
   347  	}
   348  
   349  	if err := o.Context.BindValidRequest(r, route, &o.Params); err != nil { // bind params
   350  		o.Context.Respond(rw, r, route.Produces, route, err)
   351  		return
   352  	}
   353  
   354  	res := o.Handler.Handle(o.Params, principal) // actually handle the request
   355  
   356  	o.Context.Respond(rw, r, route.Produces, route, res)
   357  
   358  }
   359  ```
   360  
   361  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:
   362  
   363  ```go
   364  // RequestBinder is an interface for types to implement
   365  // when they want to be able to bind from a request
   366  type RequestBinder interface {
   367  	BindRequest(*http.Request, *MatchedRoute) error
   368  }
   369  
   370  // AddOneParams contains all the bound params for the add one operation
   371  // typically these are obtained from a http.Request
   372  //
   373  // swagger:parameters addOne
   374  type AddOneParams struct {
   375  	/*
   376  	  In: body
   377  	*/
   378  	Body *models.Item
   379  }
   380  
   381  // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
   382  // for simple values it will use straight method calls
   383  func (o *AddOneParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
   384  	var res []error
   385  
   386  	var body models.Item
   387  	if err := route.Consumer.Consume(r.Body, &body); err != nil {
   388  		res = append(res, errors.NewParseError("body", "body", "", err))
   389  	} else {
   390  		if err := body.Validate(route.Formats); err != nil {
   391  			res = append(res, err)
   392  		}
   393  
   394  		if len(res) == 0 {
   395  			o.Body = &body
   396  		}
   397  	}
   398  
   399  	if len(res) > 0 {
   400  		return errors.CompositeValidationError(res...)
   401  	}
   402  	return nil
   403  }
   404  ```
   405  
   406  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.
   407  
   408  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).
   409  
   410  ```go
   411  // Responder is an interface for types to implement
   412  // when they want to be considered for writing HTTP responses
   413  type Responder interface {
   414  	WriteResponse(http.ResponseWriter, runtime.Producer)
   415  }
   416  
   417  /*AddOneCreated Created
   418  
   419  swagger:response addOneCreated
   420  */
   421  type AddOneCreated struct {
   422  
   423  	// In: body
   424  	Payload *models.Item `json:"body,omitempty"`
   425  }
   426  
   427  // WriteResponse to the client
   428  func (o *AddOneCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
   429  
   430  	rw.WriteHeader(201)
   431  	if o.Payload != nil {
   432  		if err := producer.Produce(rw, o.Payload); err != nil {
   433  			panic(err) // let the recovery middleware deal with this
   434  		}
   435  	}
   436  }
   437  
   438  /*AddOneDefault error
   439  
   440  swagger:response addOneDefault
   441  */
   442  type AddOneDefault struct {
   443  
   444  	// In: body
   445  	Payload *models.Error `json:"body,omitempty"`
   446  }
   447  
   448  // WriteResponse to the client
   449  func (o *AddOneDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
   450  
   451  	rw.WriteHeader(500)
   452  	if o.Payload != nil {
   453  		if err := producer.Produce(rw, o.Payload); err != nil {
   454  			panic(err) // let the recovery middleware deal with this
   455  		}
   456  	}
   457  }
   458  ```
   459  
   460  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.
   461  
   462  So to implement the AddOneHandler you could do something like this.
   463  
   464  ```go
   465  todos.AddOneHandlerFunc(func(params todos.AddOneParams, principal interface{}) middleware.Responder {
   466    created, err := database.Save(params.Body)
   467    if err != nil {
   468      return AddOneDefault{models.Error{500, err.Error()}}
   469    }
   470    return AddOneCreated{created}
   471  })
   472  ```