github.com/kaisawind/go-swagger@v0.19.0/docs/tutorial/custom-server.md (about)

     1  # Custom Server Tutorial
     2  
     3  In this tutorial we'll be building up a custom server.
     4  The core will be generated using a manually written and maintained OpenAPI 2.0 spec.
     5  The cli code will be a thin layer around that, and will simply setup the API and server,
     6  using the parsed configurations and our hand written handlers.
     7  
     8  <!--more-->
     9  
    10  The server we'll building will be very simple.
    11  In this tutorial we'll assume you are already familiar with defining an API,
    12  using the OpenAPI 2.0 yaml specification format.
    13  Please consult [the official OpenAPI 2.0 specification][OpenAPI2.0]
    14  for more information in case you're new to OpenAPI (Also known as Swagger).
    15  
    16  The end product of this tutorial can be found as `./examples/tutorials/custom-server`.
    17  
    18  The server we'll be building, will be generated using the following spec:
    19  
    20  ```yaml
    21  ---
    22  swagger: '2.0'
    23  info:
    24    version: 1.0.0
    25    title: Greeting Server
    26  paths:
    27    /hello:
    28      get:
    29        produces:
    30          - text/plain
    31        parameters:
    32          - name: name
    33            required: false
    34            type: string
    35            in: query
    36            description: defaults to World if not given
    37        operationId: getGreeting
    38        responses:
    39          200:
    40            description: returns a greeting
    41            schema:
    42                type: string
    43                description: contains the actual greeting as plain text
    44  ```
    45  
    46  As you can see, there is only 1 operation,
    47  allowing us to focus on how to create a custom server,
    48  without losing track in the details of any specific implementation.
    49  
    50  Where you store the specification is not important.
    51  By default the [swagger cli][go-swagger] expects it to be stored as `./swagger.yml`,
    52  but we'll store it as `./swagger/swagger.yml`, to keep our project's root folder clean and tidy.
    53  
    54  Once we have our OpenAPI specification ready, it is time to generate our server.
    55  This can be done using the following command, from within our root folder:
    56  
    57  ```
    58  $ swagger generate server -t gen -f ./swagger/swagger.yml --exclude-main -A greeter
    59  ```
    60  
    61  In the command above we're specifying the `-t` (target) flag,
    62  specifying that swagger should store all generated code in the given _target_ directory. We're also specifying the `-f` flag, to explicitly define that our spec file can be found at `./swagger/swagger.yml`, rather then the default `./swagger.yml`. As we are writing a custom server, we also don't want the automatically generated `cmd` server, and is excluded using the `--exclude-main` flag. Finally we're also explicitly naming our server using the `-A` flag.
    63  Please consult `swagger generate server --help` for more flags and information.
    64  
    65  Once we've executed this command, you should have following file tree:
    66  
    67  ```
    68  ├── gen
    69  │   └── restapi
    70  │       ├── configure_greeter.go
    71  │       ├── doc.go
    72  │       ├── embedded_spec.go
    73  │       ├── operations
    74  │       │   ├── get_greeting.go
    75  │       │   ├── get_greeting_parameters.go
    76  │       │   ├── get_greeting_responses.go
    77  │       │   ├── get_greeting_urlbuilder.go
    78  │       │   └── greeter_api.go
    79  │       └── server.go
    80  └── swagger
    81      └── swagger.yml
    82  ```
    83  
    84  After generation we should find only 1 sub directory in our `gen` folder.
    85  `restapi`, which contains the core server. It consists among other things out of the API (`operations/greeter_api.go`), operations (`operations/*`) and parameters (`*_parameters.go`).
    86  
    87  Note that in case we also had defined models in the global definitions section,
    88  there would be a `models` folder as well in our `gen` folder,
    89  containing the generated models for those definitions.
    90  But as we don't have any definitions to share, you won't find it in this tutorial.
    91  
    92  For more information, read through through the generated code.
    93  It might help to keep the `swagger/swagger.yml` definition next to you,
    94  to help you what is defined, for what and where.
    95  
    96  So now that we have the generated server, it is time to write our actual main file.
    97  In it we'll parse some simple flags that can be used to configure the server,
    98  we'll setup the API and finally start the server. All in all, very minimal and simple.
    99  
   100  so let's start writing the `./cmd/greeter/main.go` file:
   101  
   102  We start by defining our flags, in our example using the standard `flag` pkg:
   103  
   104  ```go
   105  var portFlag = flag.Int("port", 3000, "Port to run this service on")
   106  ```
   107  
   108  Now it's time to write our main logic,
   109  starting by loading our embedded swagger spec.
   110  This is required, as it is used to configure the dynamic server,
   111  the core of our generated server (found under the `./gen/restapi` dir).
   112  
   113  ```go
   114  swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
   115  if err != nil {
   116  	log.Fatalln(err)
   117  }
   118  ```
   119  
   120  With the spec loaded in, we can create our API and server:
   121  
   122  ```go
   123  api := operations.NewGreeterAPI(swaggerSpec)
   124  server := restapi.NewServer(api)
   125  defer server.Shutdown()
   126  ```
   127  
   128  With the server created, we can overwrite the default port, using our `portFlag`:
   129  
   130  ```go
   131  flag.Parse()
   132  server.Port = *portFlag
   133  ```
   134  
   135  After that, we can serve our API and finish our main logic:
   136  
   137  ```go
   138  if err := server.Serve(); err != nil {
   139  	log.Fatalln(err)
   140  }
   141  ```
   142  
   143  Putting that all together, we have the following main function:
   144  
   145  ```go
   146  func main() {
   147  	// load embedded swagger file
   148  	swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
   149  	if err != nil {
   150  		log.Fatalln(err)
   151  	}
   152  
   153  	// create new service API
   154  	api := operations.NewGreeterAPI(swaggerSpec)
   155  	server := restapi.NewServer(api)
   156  	defer server.Shutdown()
   157  
   158  	// parse flags
   159  	flag.Parse()
   160  	// set the port this service will be run on
   161  	server.Port = *portFlag
   162  
   163  	// TODO: Set Handle
   164  
   165  	// serve API
   166  	if err := server.Serve(); err != nil {
   167  		log.Fatalln(err)
   168  	}
   169  }
   170  ```
   171  
   172  Now that we have our server defined, let's give it a first spin!
   173  You can run it from our root directory using the following command:
   174  
   175  ```
   176  $ go run ./cmd/greeter/main.go --port 3000
   177  ```
   178  
   179  Let's now try to call our only defined operation,
   180  using the [httpie][] cli:
   181  
   182  ```
   183  $ http get :3000/hello
   184  ```
   185  
   186  Sadly this gives us the following output:
   187  
   188  ```http
   189  HTTP/1.1 501 Not Implemented
   190  Connection: close
   191  Content-Length: 50
   192  Content-Type: text/plain
   193  Date: Thu, 26 Jan 2017 13:09:52 GMT
   194  
   195  operation GetGreeting has not yet been implemented
   196  ```
   197  
   198  The good news is that our OpenAPI-based Golang service is working.
   199  The bad news is that we haven't implemented our handlers yet.
   200  We'll need one handler per operation, that does the actual logic.
   201  
   202  You might wonder why it does give us a sane response, rather then panicking.
   203  After grepping for that error message, or using a recursive search in your favorite editor, you'll find that this error originates from the greeter API constructor (`NewGreeterAPI`) found in `./gen/restapi/operations/greeter_api.go`.
   204  Here we'll see that all our consumers, producers and handlers have sane defaults.
   205  
   206  So now that we know that we just got our ass saved by go-swagger,
   207  let's actually start working towards implementing our handlers.
   208  
   209  Inspecting the `gen/restapi/operations/get_greeting.go` file,
   210  we'll find the following snippet:
   211  
   212  ```go
   213  // GetGreetingHandlerFunc turns a function with the right signature into a get greeting handler
   214  type GetGreetingHandlerFunc func(GetGreetingParams) middleware.Responder
   215  
   216  // Handle executing the request and returning a response
   217  func (fn GetGreetingHandlerFunc) Handle(params GetGreetingParams) middleware.Responder {
   218  	return fn(params)
   219  }
   220  ```
   221  
   222  Here we can read that there is a function type `GetGreetingHandlerFunc`, defined for the `getGreeting` operation which takes in one parameter of type `GetGreetingParams` and returns a `middleware.Responder`. This is the type alias our Handler has to adhere to.
   223  
   224  A bit more down we'll also encounter an interface `GetGreetingHandler`, defined for the `getGreeting` operation:
   225  
   226  ```go
   227  // GetGreetingHandler interface for that can handle valid get greeting params
   228  type GetGreetingHandler interface {
   229  	Handle(GetGreetingParams) middleware.Responder
   230  }
   231  ```
   232  
   233  Its only defined method looks very similar to the function type `GetGreetingHandlerFunc` function defined above. This is no coincidence.
   234  
   235  Even better, the `GetGreetingHandlerFunc` implements the `Handle` function as defined by the `GetGreetingHandler` interface, meaning that we can use a function respecting the type alias as defined by `GetGreetingHandlerFunc`, where we normally would have to implement a struct adhering to the `GetGreetingHandler` interface.
   236  
   237  Implementing our handler as a struct allows us to handle with a certain state in mind. You can check out the [kvstore example][] to see a more elaborate example, where you can see the handlers being implemented using a struct per operation.
   238  
   239  Our Greeter API is however simple enough, that we'll opt for just a simple method.
   240  [KISS][] never grows old. So with all of this said, let's implement our one and only handler.
   241  
   242  Back to the `./cmd/greeter/main.go` file, we'll define our handler as follows:
   243  
   244  ```go
   245  api.GetGreetingHandler = operations.GetGreetingHandlerFunc(
   246  	func(params operations.GetGreetingParams) middleware.Responder {
   247  		name := swag.StringValue(params.Name)
   248  		if name == "" {
   249  			name = "World"
   250  		}
   251  
   252  		greeting := fmt.Sprintf("Hello, %s!", name)
   253  		return operations.NewGetGreetingOK().WithPayload(greeting)
   254  	})
   255  ```
   256  
   257  Which replaces the `TODO: Set Handle` comment, originally defined.
   258  
   259  Let's break down the snippet above.
   260  First we make use of the [go-openapi/swag][] package, which is full of Goodies.
   261  In this case we use the `StringValue` function which transforms a `*string` into a `string`. The result will be empty in case it was nil. This comes in handy as we know that our parameter can be nil when not given, as it is _not_ required. Finally we form our greeting and return it as our payload with our `200 OK` response.
   262  
   263  Let's run our server:
   264  
   265  ```
   266  $ go run ./cmd/greeter/main.go --port 3000
   267  ```
   268  
   269  And now we're ready to test our greeter API once again:
   270  
   271  ```
   272  $ http get :3000/hello
   273  ```
   274  
   275  ```http
   276  HTTP/1.1 200 OK
   277  Connection: close
   278  Content-Length: 13
   279  Content-Type: text/plain
   280  Date: Thu, 26 Jan 2017 13:47:49 GMT
   281  
   282  Hello, World!
   283  ```
   284  
   285  Hurray, let's now greet _Swagger_:
   286  
   287  ```
   288  $ http get :3000/hello name==Swagger
   289  ```
   290  
   291  ```http
   292  HTTP/1.1 200 OK
   293  Connection: close
   294  Content-Length: 15
   295  Content-Type: text/plain
   296  Date: Thu, 26 Jan 2017 13:48:40 GMT
   297  
   298  Hello, Swagger!
   299  ```
   300  
   301  Great, Swagger will be happy to hear that.
   302  
   303  As we just learned, using [go-swagger][] and a manually defined [OpenAPI2.0][] specification file, we can build a Golang service with minimal effort. Please read the other [go-swagger][] docs for more information about how to use it and its different elements.
   304  
   305  Also please checkout the [kvstore example][] for a more complex example.
   306  It is the main inspiration for this tutorial and has been built using the exact
   307  same techniques as described in this tutorial.
   308  
   309  [OpenAPI2.0]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
   310  [httpie]:https://httpie.org
   311  [kvstore example]: https://github.com/go-openapi/kvstore
   312  [KISS]: https://en.wikipedia.org/wiki/KISS_principle
   313  [go-openapi/swag]: https://github.com/go-openapi/swag
   314  [go-swagger]: https://github.com/go-swagger/go-swagger