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