go-swaggerでAPI定義(swagger.yml)からサーバーのコードを生成する

[go-swagger](http://goswagger.io/)を使って、API定義(swagger.yml)からサーバーのソースコード(Go言語)を生成、APIの中身を実装していく方法についてまとめます。この投稿では、シンプルなEcho APIを実装してみるところまでをやります。

# go-swaggerのインストール
Macの場合は`brew`でインストールできます。

```
brew tap go-swagger/go-swagger
brew install go-swagger
```

Mac以外のプラットフォームの場合は[こちら](https://goswagger.io/install.html)

バージョンの確認

```sh
$ swagger version
version: 0.14.0
commit: 25e637c5028dee7baf8cdf5d172ccb28cb8e5c3e
```

# プロジェクトディレクトリの作成
```sh
mkdir swaggerexp1
cd swaggerexp1
```

# API定義(swagger.yml)の作成
`swagger.yml`を作成し、中にAPIの定義を書きます。今回は`/foo/{name}/`というパターンのURL(Method=`GET`)のAPIを一つだけ定義してみます。

```yaml:swagger.yml
swagger: "2.0"
info:
  title: "Swagger Exp 1"
  version: "0.0.1"
paths:
  /foo/{name}:
    get:
      parameters:
      - name: "name"
        in: "path"
        required: true
        type: "string"
      responses:
        200:
          description: "Successful response"
          schema:
            type: "object"
            properties:
              message:
                type: "string"
```

このAPIはURLに含まれる`{name}`という文字列のパラメータを受け取ります。返り値は`message`というstringのフィールドを含むJSONオブジェクトです。

# バリデーション
```
swagger validate swagger.yml
```

`swagger.yml`を書いたら、コード生成をする前にバリデーションをしておき、文法・内容に問題がないことを確認しておきます。必要な定義が欠如しているような場合は、エラーが表示されます。

# コード生成
```
swagger generate server -f swagger.yml -A SwaggerExp1
```

バリデーションで問題がなければ、`generate server`でサーバー側のソースコードを生成します。`-A`パラメータで指定しているアプリ名は、生成されるソースコード内で使用されます。(例: 今回の場合API全体を定義するstructが`SwaggerExp1API`になる)

## 生成されたコードの確認
コード生成を行うと、`cmd`、`models`、`restapi`以下にソースコードが生成されます。`go-swagger`を使った開発では、API定義の変更のたびにコードが生成され上書きされていくことになるため、**ここで生成されたファイルの多くは、開発者自信で中身を書き換えてはいけません**。

編集してよいのは、先頭に`// This file is safe to edit. Once it exists it will not be overwritten`というコメントがついているファイル(今回の場合は`configure_swagger_exp1.go`)のみとなります。※その代わり、このファイルは一度作成されると後にAPI定義を更新してコードを再生成しても変更が反映されないため、API定義の更新の際は手動で内容を定義に追随させていく必要があります。

# 生成されたサーバーを動かしてみる
## 必要なライブラリのインストール

生成されたサーバーを実行するためには、いくつかライブラリをインストールする必要があります。コード生成後のログにそれらのライブラリのインストールの案内が乗っていますが、`dep`を使ってしまった方が後々のことを考えると楽です。

```
dep init
```

`dep`がインストールされていない場合は`brew install dep`でインストールしましょう。(Macの場合)

## 起動してみる
```
go run cmd/swagger-exp1-server/main.go --port=8080
```

## アクセスしてみる

curlでアクセスしてみると、APIの中身が実装されていない旨がレスポンスで帰ってきます。

```
$ curl http://127.0.0.1:8080/foo/Hello
"operation .GetFooName has not yet been implemented"
```

これは`configure_swagger_exp1.go`に実装されている`/foo/{name}`に対するハンドラーが下記のような実装になっているためです。

```golang
api.GetFooNameHandler = operations.GetFooNameHandlerFunc(func(params operations.GetFooNameParams) middleware.Responder {
	return middleware.NotImplemented("operation .GetFooName has not yet been implemented")
})
```

APIの実装を進めていくためには、この`configure_swagger_exp1.go`の中身をいじっていくことになります。

# メソッドの中身を実装する
## 生成されたソースコードの確認

改めて、生成されたソースコードを確認してみましょう。

```golang:get_foo_name_parameters.go
type GetFooNameParams struct {

	// HTTP Request Object
	HTTPRequest *http.Request `json:"-"`

	/*
	  Required: true
	  In: path
	*/
	Name string
}
```

`get_foo_name_parameters.go`には、`GET /foo/name`がリクエストされた際のパラメータのパース結果を含むstructが定義されています。今回定義した`/foo/{name}`の`{name}`は、`Name`というフィールドでアクセスできるようになっています。

```
api.GetFooNameHandler = operations.GetFooNameHandlerFunc(func(params operations.GetFooNameParams) middleware.Responder {
```

リクエストのハンドラーでは、引数としてこのstructが渡されるようになっており、APIリクエスト時のパラメータはこの引数から参照します。

例

```golang
name := params.Name
```

ハンドラーが返すべきレスポンスのオブジェクトは`get_foo_name_responses.go`に定義されます。今回はレスポンスの方は一種類しか定義していませんが、結果によって複数の型を定義した場合は、ここに複数のレスポンスのstructが定義されます。

```golang:get_foo_name_responses.go
type GetFooNameOK struct {

	/*
	  In: Body
	*/
	Payload *models.GetFooNameOKBody `json:"body,omitempty"`
}

```

`get_foo_name_responses`には、`GetFooNameOK`オブジェクトを生成するための`NewGetFooNameOK()`メソッドも定義されています。

```golang
func NewGetFooNameOK() *GetFooNameOK
```

レスポンスのオブジェクトの中身は`models/get_foo_name_o_k_body.go`に定義されています。

```golang
type GetFooNameOKBody struct {

	// message
	Message string `json:"message,omitempty"`
}
```

`get_foo_name_responses.go`には、`GetFooNameOK`オブジェクトに内容を指定するための`WithPayload`メソッドも定義されているので、これらを組み合わせて下記のような形でレスポンスのオブジェクトを生成できます。

```golang
response := operations.NewGetFooNameOK().WithPayload(&models.GetFooNameOKBody{Message: "Blah Blah Blah"})
```

## APIの実装

これらを踏まえて、`GET /foo/{name}`が呼ばれた時に`{"message": "Hello, {name}"}`を返すような実装をしてみると、下記のようになります。

```golang:configure_swagger_exp1.go
func configureAPI(api *operations.SwaggerExp1API) http.Handler {
	// ...略...

	api.GetFooNameHandler = operations.GetFooNameHandlerFunc(func(params operations.GetFooNameParams) middleware.Responder {
		return operations.NewGetFooNameOK().WithPayload(&models.GetFooNameOKBody{Message: "Hello, " + params.Name})
	})

	// ...略...
}
```

curlで確認する

```
$ curl http://localhost:8080/foo/Paul
{"message":"Hello, Paul"}
```

# APIを追加する
もう一つAPIを追加してみましょう。

## 定義の追加
`swagger.yml`を開いて、`/bar`というパスのAPIを定義しましょう。今回は説明を読みやすくするため、パラメータ/レスポンス本文なしのAPIにします。

```swagger.yml

swagger: "2.0"
info:
  title: "Swagger Exp 1"
  version: "0.0.1"
paths:
  /foo/{name}:
    get:
      parameters:
      - name: "name"
        in: "path"
        required: true
        type: "string"
      responses:
        200:
          description: "Successful response"
          schema:
            type: "object"
            properties:
              message:
                type: "string"
  /bar:
    get:
      responses:
        200:
          description: OK
```

## コードの再生成
API定義を追加したら、バリデーションを行い、問題なければ再びコード生成をします。

```sh
swagger validate swagger.yml
swagger generate server -f swagger.yml -A SwaggerExp1
```

## API実装の追加
コードの再生成を行うと、新しく定義したAPI`/bar`に対応した`get_bar_parameters.go`などのファイルが新たに生成されます。しかし、`configure_swagger_exp1.go`に関しては、コードの再生成を行っても更新されません。

最初に`configure_swagger_exp1.go`が作られた時には、API`/foo/{name}`に対するハンドラーの仮実装のコードが記入されていたため、その内容を書き換えて実装を進めればよかったですが、API追加時には自分でハンドラー登録のコードを書く必要があります。

### APIハンドラーの確認
`configure_swagger_exp1.go`では`operations.SwaggerExp1API`のインスタンス`api`に対して、各API(パス)に対応したハンドラーを登録しています。`swagger_exp1_api.go`を開くと、`operations.SwaggerExp1API`に登録できるハンドラーの名前を確認できます。今回追加した`GET /bar`に関する定義は下記のようになっています。

```golang:swagger_exp1_api.go
// GetBarHandler sets the operation handler for the get bar operation
GetBarHandler GetBarHandler
```

`GetBarHandler`に関する型の情報は`get_bar.go`で確認できます。

```golang:get_bar.go
// GetBarHandlerFunc turns a function with the right signature into a get bar handler
type GetBarHandlerFunc func(GetBarParams) middleware.Responder

// Handle executing the request and returning a response
func (fn GetBarHandlerFunc) Handle(params GetBarParams) middleware.Responder {
	return fn(params)
}

// GetBarHandler interface for that can handle valid get bar params
type GetBarHandler interface {
	Handle(GetBarParams) middleware.Responder
}
```

レスポンスの型、メソッドは`get_bar_responses.go`で確認します。

```golang:get_bar_responses.go
/*GetBarOK OK

swagger:response getBarOK
*/
type GetBarOK struct {

}

// NewGetBarOK creates GetBarOK with default headers values
func NewGetBarOK() *GetBarOK {

	return &GetBarOK{}
}
```

### APIハンドラーの登録

上記確認した定義を元に、`configure_swagger_exp1.go`に手動でハンドラーを登録するコードを書きます。

```golang:configure_swagger_exp1.go`
api.GetBarHandler = operations.GetBarHandlerFunc(func(params operations.GetBarParams) middleware.Responder {
	return operations.NewGetBarOK()
})
```

### 動作確認
サーバーを起動し直したら、`curl`で新しく追加したAPIを叩いてみましょう。

```
curl http://localhost:8080/bar
```

# 参考リンク
* [go-swagger/go-swagger](https://github.com/go-swagger/go-swagger)