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 ```