OpenAPI Specification(以降OASとします)はREST APIの仕様を定義する仕組みです。
YAML等のフォーマットで仕様を記述し、ドキュメントや各種言語向けのコードを生成して使います。おかしな仕様にはエラーを出してくれるし、入力値の取得やバリデーションなど自動生成すべきコードをいい感じに用意してくれるので、とても便利です。わかりやすく言うとREST APIのための形式仕様記述(formal specification)1です。
OASで定義したAPI仕様からGoのコードを生成することを考えます。
OpenAPI GeneratorのGo対応は微妙な感じです。ググるとgo-swaggerがよく使われているようですが、OAS 3.0の前身であるSwagger 2.0にしか対応していません。OAS 3.0に対応したものはoapi-codegenがあります。READMEやFuture Tech Blogの記事(本記事がいらないくらい詳しい)がよさげだったので使ってみたところ、普通に使えたので備忘録がてらまとめておきます。
前提
- oapi-codegen v1.5.0 (Feb 09, 2021)
- chiルータを利用
- oapi-codegen自体はEcho, Chi, net/httpに対応している
- サーバコードの生成だけ
- 今回はクライアントをGoで実装しなかったので
- APIキーで認証
※ 以下、一部でGitHubからコードを引用しています。ライセンス等は引用元を参照してください!
コード生成と実装
コード生成
公式のexamplesがめっちゃわかりやすいです。↓に引用します。
このようにgo generateでやるか手動で実行するかですね。
基本形は↓です。
| |
これを実行するとServerInterfaceを含めたコードを生成します。あとはこのインタフェースを満たす実装(後述)をするだけです。生成されるコードが薄くて普通に読めるのが良いですね。
よく使うはずのオプションを入れると↓こうなります。
| |
-generate: 何を生成するかtypeschi-serverspecあたりですかね?clientはサーバコードの場合不要- Echoなら
chi-serverの代わりにserver specはOASのdump、Validatorに必要
-include-tags: OASのタグを見て、指定したタグだけ生成するFugaは別で実装したいんだよね〜ってときにHogeのコードだけ生成できます- ただし、別々で生成した
HogeとFugaを同じchiルータで動かすのは大変そうです(後述)。
実装
↓の公式の例のように、ServerInterfaceを満たす実装をするだけです。
Goのtipsとして、パッケージ変数にvar _ ServerInterface = (*PetStore)(nil)を定義しておくと何が足りないのかがわかって便利です。
呼び出し側も公式の例がわかりやすいのですが、1)OpenAPIを読み込んで、
2)OapiRequestValidatorに食わせてValidatorのMiddlewareを生成します。
なので、-include-tagsでHogeとFugaを別々に生成した場合、同じchiルータで動かすのが難しいです。
細かい話なのでクリックで展開
OapiRequestValidatorがswaggerごと(HogeとFugaで異なる)なので、HogeValidator->FugaValidator->リクエストハンドラという流れになり、Fuga宛のリクエストはHogeValidatorに弾かれます。
ルーティングをがんばって設定すればできるんですかね🤔
CORSとPreflight
Middlewareを使いました。試しにYAMLにOPTIONSメソッドを書いてみたところ、なにも生成されませんでした。
これってAmazon API GatewayやCloud Endpointsではどうするんだろう?PreflightでOPTIONSが飛んでくるのはAPIの仕様ではなくブラウザの仕様だし?詳しい人教えてください。
注意点は、CORSのMiddlewareをOapiRequestValidatorの前に入れる必要があることです(OapiRequestValidatorでOPTIONSが弾かれるため)。
| |
Heartbeat
K8sにデプロイする場合などで必要になると思います。これもAPIの仕様じゃないと思うので、Middlewareを使いました。
| |
middleware.Heartbeatを使う代わりに自前で実装してもいいでしょう。
| |
認証
↓こんな感じでOptions.AuthenticationFuncをセットすればとりあえず動きます。
| |
どうやらOASでメソッドにsecurityが定義されていたらOptions.AuthenticationFuncを見に行くようなので、適切なAuthenticationFuncをセットしておく、でよさそうです。詳細は↓
クリックで展開
↓のcase *openapi3filter.SecurityRequirementsErrorの元をたどっていくと、
↓でOptions.AuthenticationFuncを取り出していて、そのあと実行しています。
おわりに
oapi-codegen、生成されるコードが簡潔だし使い方も簡単なのでオヌヌメです。今回作ったものはそのうち書けたら書きます。
厳密な定義はよくわからないですが、仕様が破綻していたらエラーを出してくれるので形式仕様記述ですよね?🤔 ↩︎