27.3K Views
December 10, 23
スライド概要
UnityでOpenAPI Generatorを使う
UnityでOpenAPI Generatorを使う 2023/12/10 Unity大人のLT torisoup
自己紹介 • とりすーぷ • 株式会社バーチャルキャスト • メタバース作ってます • 本とか • 著書「UniRx/UniTask完全理解」 • Unityバイブル 編集委員 • MS MVP 2018~
今回の話 • UnityでOpenAPI Generatorを使ってみる話
もくじ • OpenAPI Generatorって何 • OpenAPIとは • OpenAPI Generator • 実際に使ってみる • 感想
「OpenAPI」って何? • REST APIのインタフェース定義やツール群のこと • 「HTTPで実行できるAPI」についてのインタフェース定義の規定 • 定義ファイルの視認性を上げるWebUI • 定義ファイルからのコードジェネレータ
OpenAPIとSwagger • OpenAPIの前身がSwagger • 元々「Swagger」と呼ばれていたものが「OpenAPI」に改名 • 現在ではSwaggerと呼ぶとOpenAPI周辺で使えるツール群を指す場合が多い
OpenAPIの表記方法 • APIの定義フォーマットを「OpenAPI Spec」と呼ぶ • 昔はSwagger Specとも呼んでいた • JsonとXMLの表記がある • 人間が手で書いてもいいし、コードから自動生成でも可
Specの定義例(json表記)
Specの定義例(json表記) 「GET /users」という APIがあるよ 成功時は200だよ Userのデータ構造 String型の「name」 整数型の「age」 成功時は 「User配列」が 返ってくるよ
Swagger UI • OpenAPI Specで記述したAPI定義をWeb UIで 確認/動作確認ができるツール • Editor+UIのWebサービス:https://editor.swagger.io/ • Specをコピペで貼り付ければ動作確認できるようになる
Swagger UI
Swagger UI
OpenAPI Generator • OpenAPI Specで記述されたAPI定義から 自動的にクライアントコードを生成するツール • 様々な言語に対応(C#も対応) • Unityでも一応使える
GitHub • https://github.com/OpenAPITools/openapi-generator
どういうときに使うのか? • 例:Webサーバ開発とクライアント開発の連携 • 先にSpecでAPI定義を書いて双方でレビュー、 レビューが通ったらサーバサイドに実装、 クライアントはコードジェネレートして対応、みたいなフローが作れる • 他にも実装済みのコードからOpenAPI Specを自動生成して クライアントチームに渡す、みたいなやり方も全然あり
OpenAPI Generatorの導入 • https://openapi-generator.tech/docs/installation/ • npm, Homebrew, Scoop, Dockerなどなど好きな方法で導入して • jarファイルを直接DLして使うでもOK • 今回はScoopで導入
導入(Scoop) • 書いてあるとおりにやるだけ
実際にUnityで使ってみる
バージョン • Unity • 2022.3.8f1 • OpenAPI Generator • 7.1.0
サンプルのHTTPサーバ • ASP.NETでサンプルサーバを作成 • WebAPIテンプレートでプロジェクトを作ったらSpecの自動生成やSwaggerUIもついてきた
APIの概要 • User情報の登録と参照ができる • User: NameとAgeを持つ • API • GET /users : 全件取得 • GET /users/{name} : 名前で探して取得 • POST /users : Userの新規登録
実行例 $ curl --insecure http://localhost:5086/users [{"name":"Alice","age":20},{"name":"Bob","age":21}]
実装とSpec 自動生成したこの OpenAPI Specから クライアントコードを生成してみる
1. ディレクトリを用意する • どこでもいいのでディレクトリを用意 • Specなりconfigファイルを置く場所です • 管理しやすい場所ならどこでもいいです • 今回はUnityのプロジェクト内に配置 • 名前もなんでもいいです • 今回は「tools」という名前にした
2. Specを配置する • さっき作ったディレクトリにSpecファイルを配置 • Jsonでもxmlでもどっちでもいいです
3. config.jsonを定義して配置する • 「config.json」を作ってこれも配置 • これも名前はわかりやすければ何でもいいです • 一旦中身は空でOK
4. config.jsonの中身を定義する • https://openapi-generator.tech/docs/generators/csharp/ • ここを参考に設定を定義する
今回の定義例
今回の定義例 自動生成したときの namespaceに相当する
今回の定義例 OpenAPI Generator 7.1.0だと バグっててここの設定が無視されてるっぽい
今回の定義例 「restsharp」が一応stableらしい
今回の定義例 「restsharp」が一応stableらしい 「unityWebReuqest」もあるが Experimentalすぎるので今回はやめておく
今回の定義例 Validationコードの生成 不要なのでOFFにする
6. ジェネレート実行コマンドを定義 • 適当にバッチファイルを作る • 今回は「tools/」にgenerate.batを用意した • Macなどの場合はシェルスクリプトでOK
7. 実行コマンドを書く
7. 実行コマンドを書く ごちゃごちゃ書いてあるけど 重要なのここ
7. 実行コマンドを書く 出力時の名前空間、 作業用パス、 出力先パスの指定
補足 • ジェネレートするといろいろ生成されるので、Unityで使うcsコードだけコピーしてる • ついでに拡張子を自動生成ファイルだとわかるように「***.g.cs」に置換
7. 実行コマンドを書く ファイル名をリネームして Unityのプロジェクトにコピー
ついでに • Unity側でasmdefの定義しておくと良さげ • なくてもいいけど定義したほうが管理はしやすい
8.必要なライブラリをUnityに導入 • 今回は「RestSharp」を使う版でジェネレートしたので、 RestSharpを動かすためのプラグインを入れる • 手動で解決するか、NugetForUnityなどで入れる
補足)手動でプラグイン導入 • NugetからパッケージをDLして、拡張子をzipにして展開 中身のdllをUnityにコピー
補足)手動でプラグイン導入 • NugetからパッケージをDLして、拡張子をzipにして展開 中身のdllをUnityにコピー .NET Standard 2.x向けを 選んでおけばたぶん大丈夫
9. Unityから実行してみる
BasePathを指定して 「User」にアクセスするAPIクライアントを作成
BasePathを指定して 「User」にアクセスするAPIクライアントを作成 APIクライアントはSpecの Tags名を基準として生成される 今回は「UserApi」が生成された
実際にサーバ通信する部分
「User」クラス
Userの登録APIの実行 「Alice」と「Bob」を登録 登録済みUserの全件取得
実行結果 登録したAliceとBobの取得ができた
感想とか補足とか
ジェネレートコードへの依存 • 直接依存させるのは怖いので避けたい • 最低限Adapterを挟む。依存関係の逆転までできるとなお良い。 △ Generated Code ◎ ◯ Generated Code Generated Code Adapter Adapter My Class My Class ダイレクトに依存。 再ジェネレートするたびに My Class側に修正が必要になる 可能性がありオススメしない。 Adapterを介するやり方。 再ジェネレートしたときの差分を Adapterで吸収できる。 My Class My Interface 依存関係を逆転させるやり方。 ここまでできればベスト。
オプション:optionalEmitDefaultValues • ジェネレート時のConfig要素にある
オプション:optionalEmitDefaultValues • 型の既定値をデータ構造に含めたときにそれをシリアライズするかどうか? • false : 既定値の場合は含めない (← デフォルトはこっち) • true: 既定値であってもシリアライズ/デシリアライズに含める false => true => {"name": "Alice"} {"name": "Alice","age": 0}
library ジェネレートコードが依存する ライブラリはいくつか選べる defaultはrestsharp
他の「library」はどうなのか? • RestSharp / HttpClient • RestSharpはHttpClientのラッパーなので本質は同じ • どっちもUnityで動くには動く
ただし… • どちらを使ってても、大量に通信しようとすると 通信が完全に詰まってawaitが返ってこない現象が起きた • 原因不明 • Debuggerで追うとC#のHttpClient内部で止まってしまっているっぽい • HttpClientHandlerで設定を変えようと試したが、 Unity monoではそこの変更メソッドが未実装のため変更不可だった
詰まり問題、どうしたか • バイナリを大量に通信する部分はUnityWebReuqestで手動実装した • 「サイズが大きいバイナリをダウンロードする」 「短期間で一度に大量のリクエストを送る」 といった可能性がある場所はUnityWebReuqestに差し替えたら詰まらなくなった • 不定期にたまに叩くAPI程度ならOpenAPI Generatorの自動生成コードでも問題はない
他の生成形式としてUWRがあるが… • unityWebRequest • experimental • 自分は使ってないのでわからない • そもそも一般的に使われているのかもわからない • 全く枯れてないので未知数
そもそも… • OpenAPI Generator、品質がかなり怪しい • よくぶっ壊れている • 一部パラメータを無視して動いてたり、 デシリアライズが中途半端で通信はできるけど結果が取り出せなかったり • プロダクト規模に対してメンテナンスが追いついてない空気 • 本当によく壊れてる • バグを見つけたら自分で直してプルリクを投げつけるスタイルでないと厳しい • プルリクを出しても放置されがち(最近は改善されたっぽい?)
実際にプロダクトに使った感想 • Specの定義とコード生成は便利ではある • サーバAPIをクライアントチームがレビュー、などができる • Spec定義さえあればコード生成してクライアント実装が進められる • 生成されたコードが素直に動くことがなかった • 「RestSharp」のときは概ね問題がなかった • が、先程の「詰まり問題」が発生したため「HttpClient」に差し替えた • 「HttpClient」で生成されたコードは問題だらけで手動修正が必要だった • そもそもこっちはexperimentalなのでしょうがないが… • 結局こっちにしても「詰まり問題」は解決しなかった
まとめ • OpenAPIとOpenAPI Generator • REST APIのインタフェースを定義するフォーマット • それのコードジェネレータ • UnityでOpenAPI Generatorを使う • 周辺ライブラリの依存解決がかなりめんどくさいし管理が大変 • よく壊れてるので生成されたコードが素直に動けばラッキー くらいの気持ちでいたほうがよい