ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Swift OpenAPI Generatorの紹介
iOSアプリを拡張する場合でも、Swiftでサーバを書いている場合でも、Swift OpenAPI GeneratorがHTTPサーバAPIの扱いにどのように役立つかを紹介します。このパッケージプラグインを利用してOpenAPIドキュメントからコードを生成することでワークフローを合理化し、コードベースを簡素化する方法を説明します。
関連する章
- 0:44 - Considerations when making API calls
- 1:52 - Meet OpenAPI
- 6:15 - Making API calls from your app
- 12:33 - Adapting as the API evolves
- 14:23 - Testing your app with mocks
- 16:12 - Server development in Swift
- 19:24 - Adding a new operation
リソース
- Swift OpenAPI Generator package plugin
- Swift OpenAPI Generator Runtime
- URLSession Transport for Swift OpenAPI Generator
関連ビデオ
WWDC22
-
ダウンロード
♪ ♪
Swift on Serverチームのサイです このセッションではSwift OpenAPI GeneratorがサーバAPIの操作に どのように役立つかを説明します iOSアプリを拡張する場合でもSwiftで バックエンドサーバを作成する場合でも この新しいSwiftパッケージプラグインで ワークフローの合理化や コードベースの簡素化を実現できます 今年はデバイス上のデータの操作が これまで以上に簡単になりました 実装したい機能には サーバコンポーネントによって 提供される動的コンテンツが 必要になる場合があります これはリモートサービスに ネットワークリクエストを 送信してそのAPIを 呼び出すことを意味します 適切なネットワークリクエストを行うには 考慮すべきことがたくさんあります サーバのベースURLは何か APIエンドポイントを構成する パスコンポーネントは何か どのHTTPメソッドを使用する 必要があるか パラメータはどう指定すればよいのか これらはサーバAPIを呼び出すときに 考慮すべき事項の一部にすぎません より複雑なAPIの場合はさらに多くの ことを考慮する必要があります これらの質問にどう答えるかです
ほとんどのサービスには何らかの APIドキュメントがあります 手書きの文書は不正確だったり 古くなったりすることがよくあります 特にサービスが急速に 進化している場合など ソースコードにアクセスできる場合は 実装を確認するか APIを手動で確認することもできます しかしこれではサービスの動作が 不完全になってしまう可能性があります サポートフォーラムの検索や他の 組織の知識に頼ることも可能でしょう ただし善意のある人でも十分な 情報が得られていない可能性や 一貫性のない回答が出てきたり 最初よりも多くの疑問が 残ることもあります これらのリソースはある程度参考には なるものの全体像はつかめません APIの正式で構造化された説明を使えば 曖昧さを排除するのに役立ちます そこでHTTPサービスを定義するための オープン仕様であるOpenAPIが活躍します
業界標準であるOpenAPIは 広く採用されており 成熟しているため APIの操作に 役立つ確立された規則と ベストプラクティスとして利用できます OpenAPIを使用するとサービスの動作を YAMLまたはJSONで文書化し 機械可読形式により ツールの豊富なエコシステムから 多くの利点が得られます テスト生成やランタイム検証 あるいは相互運用性などで利用できる 多くのツールがあります OpenAPIが特に知られている ものの1つに インタラクティブなドキュメント 生成ツールがあります OpenAPI発展の主な動機には 採用するデベロッパが 仕様主導の開発で利用するための コード生成があります 先ほどのAPIエンドポイントの例ですが このリクエストを受信すると サーバはパーソナライズされた 挨拶をJSONオブジェクトで返します このAPIを呼び出すためのコードを コード生成を使用せずに 記述してみましょう
まずコンポーネントに変換する サーバのベースURLが 何であるか 次にパスコンポーネントを 追加してAPIエンドポイントを 構築してパラメータをクエリ項目 として指定します 次にURLRequestを構築し URLSessionによりHTTPリクエストを 作成します
次に期待されるタイプの応答を 検証する必要があり それには予期されるステータスコードと コンテンツタイプが含まれます
次に応答からバイトをデコードしますが これを行うにはDecodeableに 準拠するSwift型を定義し JSONDecoderを使用します 最後に応答からメッセージ プロパティを返します
このコードを書くのは問題ありませんが これは単純なAPI操作に対する リクエストの1つにすぎません 現実世界のAPIの多くには何百もの操作があり 多数のリクエストや レスポンスタイプ ヘッダフィールド パラメータなどが存在します 操作ごとにこのコードを記述するとなると 繰り返し作業が発生し 冗長でエラーが発生しやすくなります コードベース内のこうした処理は アプリの核となるロジックを損ないます
OpenAPIではツールを使用して このコードの大部分を生成できるため ユーザー操作に関わるコードに 集中できます サンプルAPIを使用してOpenAPI ドキュメントを調べてみましょう OpenAPIドキュメントはすべて 使用している OpenAPI仕様のバージョンを 宣言します APIの名称やバージョン サーバURLの リストなどAPIに関する メタデータが提供されます 次にAPIを構成するパスと HTTPメソッドをリストします このAPIにはgetGreetingという 名称のオペレーションが1つ 挨拶パス上のGETメソッドの 動作を定義します この例ではサーバは常に 200を応答し OKのHTTPステータスコード なのでJSONオブジェクトを返し JSONスキーマを使用して 定義されます
この図ではシンプルにしていますが 操作には 複数の応答が含まれることもあり ステータスコードとコンテンツ タイプが異なる場合など エラーが発生した場合に 何が起こるかを含む 考えられるすべてのシナリオを 文書化できます 操作がパラメータを受け入れる場合 OpenAPI文書にそれを 含めることもできます
この操作は挨拶をパーソナライズ するために使用される文字列値の 「name」という任意の クエリパラメータをサポートします
Swift OpenAPI Generatorの助けを借りて はるかに少ないコードで同じAPIの 呼び出しを行うことができます
タイプセーフな入力を 使用しており 出力値が豊富な列挙型であるため コンパイラが文書化されたすべての 応答とコンテンツタイプを 確実に処理するのに役立ちます 応答本文内の関連付けられた値は value-typeで タイプセーフなプロパティを 備えています 入力のエンコードやリクエストの作成 応答の解析や出力のデコードに 関連するものはすべて生成された コードによって処理されます
Swift OpenAPI Generatorは ビルド時に実行される Swiftパッケージプラグインです 生成されたコードは常にOpenAPI ドキュメントと同期されており ソースリポジトリにコミットする 必要はありません Swiftパッケージプラグインの詳細については 「Meet Swift package plugins」 をご参照ください iOSアプリでSwift OpenAPI Generatorを使用する 方法を見てみましょう これにはまず呼び出し可能なAPIが 必要になります このデモでは10個の猫の顔の 絵文字のうち1つを ランダムに返す単純な APIを呼び出します タップするたびにサーバから 新しいサンプルコンテンツを取得して 大きな絵文字とボタンに 置き換えるというSwiftUIアプリの テンプレートから始めます 実行されているサーバは localhostをリスニングしていて localhostはターミナルから curlを使用してクエリできます
これが素晴らしいAPIである ことは否定できません でもそれがさらに優れているのは OpenAPIで定義されていることです まったく異なる種類の猫で このサービスのOpenAPIドキュメントを 表示してみましょう
このAPIにはgetEmojiという名称の オペレーションがあり これをアプリから呼び出して UIを更新します まず始めにXcodeに切り替えます
このサンプルiOSアプリにはSwiftUIを 使用して定義された基本的な― UIがありXcodeプレビューで 確認できます 次の数分でUIコンポーネントを サーバから取得する― 動的コンテンツに置き換えます またSwift OpenAPI Generatorで API呼び出しを行うために 手動で記述する必要がある コードを簡素化します まず必要なパッケージの依存関係を プロジェクトに追加します 次にコード生成にプラグインを使用 するようにターゲットを構成し OpenAPIドキュメントと プラグイン構成ファイルを ターゲットソースディレクトリに追加します プロジェクトが構成されたら UIコンポーネントを置き換え 生成されたクライアントタイプにて サーバへのAPI呼び出しを行います Swift OpenAPI Generatorを 使用するようにアプリを構成するには Project Editorに移動し タブを選択し 新しい依存関係を追加します
このデモではローカルパッケージ コレクションを使用していますが パッケージのURLはセッション ノートに記載されています まずパッケージプラグインを提供する swift-openapi-generatorへの 依存関係を追加します
生成されたコードで使用される 共通の型と抽象化を提供する swift-openapi-runtimeへの 依存関係を 続いて追加します
また生成されたコードは特定のHTTP クライアントライブラリには未関連なので 使用したいライブラリの統合パッケージ を選択する必要があります iOSアプリを構築しているため URLSessionパッケージを使用しますが 他の例や自分で書く場合については ドキュメントを確認してください 依存関係を適切に設定すると OpenAPI Generatorプラグインを 使用するようにターゲットを構成できます 「Target Settings」で 「Build Phases」を選択し 「Run build-tool plugins」という セクションを開きます クリックして新しいプラグインを追加し リストからOpenAPI Generatorを選びます
プラグインはターゲット ソースディレクトリに 2つの入力ファイルを必要とします OpenAPIドキュメントと プラグイン構成ファイルです これをプロジェクトに追加します
プラグイン構成はプラグインが 生成するコードを指定する シンプルなYAMLスキーマを 使用して記述されます この場合生成する「types」は OpenAPIドキュメントから派生した 再利用可能なタイプです また任意のHTTPクライアントで API呼び出しを行うために 使用するクライアントコードも生成します ContentView.swiftに戻り プロジェクトを再コンパイルして 生成されたコードをアプリで 使用できるようにします
セキュリティ対策としてプラグインを 初めて使用するときには プラグインを信頼するか どうかを尋ねられます
プロジェクトを再コンパイルしたので UIコンポーネントを置き換え生成された クライアントタイプを使用して サーバへのAPI呼び出しや ビューの更新を行うことができます
まず新しい状態プロパティを 絵文字のビューに追加し プレースホルダー値で 初期化します 次に地球儀の画像を 絵文字の含まれるテキストビューに 置き換え「Hello, world」 メッセージをボタンに置き換え ビューのボタンのスタイルを 設定します
生成されたコードはAPI呼び出しを 行うために使用できる Clientという名称のタイプを 提供します ただしその前にOpenAPIランタイムと トランスポートモジュールを インポートする必要があります
これでクライアントプロパティをビューに 追加してOpenAPIドキュメントで 定義されているローカルホストサーバURL を使用するように ビューを構成するイニシャライザを 追加できました
次にこのクライアントを使用して サーバにAPI呼び出しを行うための 関数を追加します
APIリクエストを行うために手動で記述する 必要があるコードはこれですべてです それ以外はすべて生成された コードによって処理されます 応答は文書化されたすべての応答を モデル化する型の列挙値で あらゆるシナリオに対応するよう コンテンツタイプを設定します switchステートメントを使用して 応答本文から絵文字を抽出する 必要があります
何かが欠けているようです コンパイラからすべてのシナリオが処理 されていないというエラーが表示されました 欠落しているswitchケースを Xcodeに埋めさせます
サーバが応答したものが OpenAPIドキュメントに 指定されていないものだった場合でも それを適切に処理する方法があります このデモではコンソールに 警告を出力し 絵文字を猫以外のものに更新します
これでボタンがタップされたときに この関数を呼び出せます
またボタンで新しい猫の絵文字を 取得してUIを更新できます
新しい機能がサーバに追加されると そのAPIも進化します サーバがOpenAPIにより 文書化されている場合 Swift OpenAPI Generatorの使用で アプリからの新機能の利用が 簡単になります OpenAPIドキュメントの進化に 応じてアプリを更新する 方法の例を見てみましょう
絵文字に関しては多ければ 多いほど良いため サービスAPIを拡張して新しいオプションの クエリパラメータ「count」で 複数の絵文字を取得できる ようになりました
1匹ではなく3匹の猫を取得する 別のボタンを使用してアプリを拡張します
まずOpenAPIドキュメントに パラメータを追加します プロジェクトを再コンパイルすると パラメータをアプリで 使用できるようになります 次にこのパラメータを使用してAPI 呼び出しを行う新しいボタンを作成 まず新しいパラメータをOpenAPI ドキュメントに追加します
このパラメータの名称は 「count」です これはオプションのパラメータです URLクエリの一部として提供されるもので 整数値です ContentView.swiftに戻って パラメータも受け取るように updateEmoji関数を 拡張しましょう
API呼び出しを行うときにも このパラメータを使用します
ボタンを複製してラベルを 「その他の猫」に変更します
このボタンがタップされると 同じ関数が呼び出されますが 今回はカウントが3です
プレビューでは「猫を表示」をタップして 1匹の猫を表示するか「その他の猫」を タップして3匹表示できます これまでずっと実際のサーバに リクエストを行ってきましたが 特に開発中は必ずしも実用的 または望ましいとは限りません 生成されたクライアントタイプは Swiftプロトコルに準拠しているため ネットワーク接続やライブラリ 移動を必要としないモックを 簡単に作成できます 生成されたプロトコルの名称は APIProtocolなので このプロトコルを採用している 新しいMockClientタイプを まず定義します 次にAPIProtocolに準拠する 型に対して汎用となるように ビューを更新してから 依存関係の注入をサポートするために イニシャライザを更新します 次にXcodeでUIをプレビューする際 MockClientを使用します ここでMockClientタイプを 宣言します
この型はAPIProtocolを採用すると 宣言しているため コンパイラはプロトコル要件を 満たしていることを確認します API操作に不足しているハンドラを Xcodeに追加させます
そして実際のサービスと区別するために ロボットの絵文字を返す ビジネスロジックを追加します
これでこのプロトコルに準拠する型に 対してビューをジェネリックにし ジェネリック型パラメータを 使用するようクライアント プロパティを更新できるようになりました
クライアントをパラメータとして受け取る イニシャライザを追加し 汎用のwhere句で既存の イニシャライザを更新するので クライアントが指定されていない場合は 以前と同じものが使用されることになります
アプリが起動されると引き続き 実サーバが使用されますが XcodeでUIをプレビューするときに MockClientを挿入できるようになりました
UIプレビューでボタンをタップすると 猫の代わりにロボットが表示され ネットワーク接続やサーバの 実行は不要になります
モッククライアントを追加するまで iOSアプリはローカルマシンで 実行されている実際のサーバに リクエストを送信していました このサーバもSwift OpenAPI Generatorを 利用してSwiftで作成されたものです サーバは単純なSwiftパッケージであり コード生成にSwift OpenAPI Generator パッケージプラグインを使用します 生成されたサーバコードを使用する ためにAPIProtocolという 名称の生成プロトコルに 準拠する型を定義し API操作のビジネス ロジックだけを実装しました サーバを設定するためには ビジネスロジックを提供するハンドラでの API操作用に受信したHTTP リクエストを接続するために 生成された関数 registerHandlersを使用します 見てみましょう
コンソールを展開するとデモiOS アプリからの実際のリクエストが表示されます
サーバを実装するために手動で記述する 必要のあるSwiftコードはこれですべてです このサービスを文書化するために OpenAPI を使用する代わりにOpenAPI ドキュメントから開始しSwift OpenAPI Generatorを使用し API仕様を実装するサーバの 作成を簡素化します
生成されたAPIProtocolに準拠し API操作のビジネス ロジックのみを提供するタイプを 定義しました API エンドポイントの場合 生成された 関数を使用してそのメソッドを HTTPサーバに 使用しました このデモではSwift用のオープンソース フレームワークであるVaporを使用 生成されたコードはSwift OpenAPI Generatorの 統合パッケージを提供する任意の Webフレームワークで使用できます 他のオプションと独自のオプションを 作成する方法については ドキュメントを参照してください
メイン関数ではOpenAPI トランスポートの作成に使用する 新しいVaporアプリケーションを 作成します 次にハンドラ型のインスタンスを作成し 生成されたregisterHandlers 関数を使用し API操作ごとにHTTPサーバ内で ルーティングを設定 そうでない場合は手動で 行う必要があります 最後に手動で設定したのと 同じ方法でVaporアプリを 実行します
Swiftはサーバ開発に 最適な言語で Swiftでのバックエンド サービスの作成に関する詳細は 「サーバサイド開発にXcode を使用」というセッションをどうぞ
Swift OpenAPI Generatorを使用 するパッケージ構成を見てみましょう サーバはSwif パッケージ として実装され Package.swiftファイルを 使用して定義されます このパッケージにはSwift OpenAPI Generatorプラグインを使用する CatServiceと呼ばれる単一の 実行可能ターゲットがあります 生成されたサーバコードはランタイム ライブラリの型と抽象化に 依存しており任意のWeb フレームワークで使用できます 統合パッケージを提供するためこの ターゲットには swift-openapi-runtimeや swift-openapi-vaporに Vapor自体について依存関係があります ターゲットソースディレクトリに デモiOSで使用したものと 同じOpenAPIドキュメントと プラグイン設定ファイルを 追加しました このターゲットでは型とサーバ スタブを生成しています このサービスに新しい機能を追加 する際に仕様主導型開発では どう簡素化できるかを 見てみましょう
猫の絵文字は素晴らしいものですが ネットは猫の動画を交換するために 構築されたことを示す証拠があるので その機能をサーバに追加します
仕様主導の開発では 新しいAPI操作の追加に 必要なのは2つの手順のみです まず新しい操作を OpenAPIドキュメントに追加します 次に 生成されたプロトコルには 新しい機能要件があるため コンパイラはハンドラにメソッドを 定義するようリクエストし ビジネスロジックを実装します 始める前に ターゲットのリソースフォルダに 追加した猫の動画ファイルが必要です
OpenAPIドキュメントに移動し 新しい操作を追加します
この操作はgetClipと呼ばれ バイナリ応答を持ち content-typeは応答本文に動画 データが含まれていることを示します
パッケージを再コンパイルしようと すると失敗します
これはハンドラが新しい操作のための 機能を提供していないので 生成されたプロトコルに準拠 しなくなっているためです Xcodeにプロトコルスタブを入力させ 動画リソースファイルからバイトを読み取り バイナリ本文を含むOK応答を返す ビジネスロジックを提供します タイプセーフに生成されたコードでは この関数からのバイナリ応答本文の 戻り値のみが許可されますが それはこの操作が OpenAPIドキュメントで指定 されているものであるためです パッケージを再コンパイルすると 正常処理され サーバを再起動できます Safariに切り替えると新しい APIエンドポイントをテストできます
OpenAPIによるサービスの文書化を 見てきましたが 曖昧さを排除し仕様主導の開発を 可能にするのに有用です Swift OpenAPI Generatorが iOSアプリのサーバAPI操作で どのように簡素化できるかも見ました Swiftの言語機能や成長を 続けるSwift-on-server エコシステムついても見てきました バックエンドサービスの 実装には最適です オープンソースのSwift OpenAPI Generatorは GitHubから入手できますので 詳細をご確認いただき 成長を続けるプロジェクトに お役立てください ご視聴ありがとうございました 以上ですニャ
-
-
4:17 - Example OpenAPI document
openapi: "3.0.3" info: title: "GreetingService" version: "1.0.0" servers: - url: "http://localhost:8080/api" description: "Production" paths: /greet: get: operationId: "getGreeting" parameters: - name: "name" required: false in: "query" description: "Personalizes the greeting." schema: type: "string" responses: "200": description: "Returns a greeting" content: application/json: schema: $ref: "#/components/schemas/Greeting"
-
7:05 - CatService openapi.yaml
openapi: "3.0.3" info: title: CatService version: 1.0.0 servers: - url: http://localhost:8080/api description: "Localhost cats 🙀" paths: /emoji: get: operationId: getEmoji parameters: - name: count required: false in: query description: "The number of cats to return. 😽😽😽" schema: type: integer responses: '200': description: "Returns a random emoji, of a cat, ofc! 😻" content: text/plain: schema: type: string
-
8:03 - Making API calls from your app
import SwiftUI import OpenAPIRuntime import OpenAPIURLSession #Preview { ContentView() } struct ContentView: View { @State private var emoji = "🫥" var body: some View { VStack { Text(emoji).font(.system(size: 100)) Button("Get cat!") { Task { try? await updateEmoji() } } } .padding() .buttonStyle(.borderedProminent) } let client: Client init() { self.client = Client( serverURL: try! Servers.server1(), transport: URLSessionTransport() ) } func updateEmoji() async throws { let response = try await client.getEmoji(Operations.getEmoji.Input()) switch response { case let .ok(okResponse): switch okResponse.body { case .text(let text): emoji = text } case .undocumented(statusCode: let statusCode, _): print("cat-astrophe: \(statusCode)") emoji = "🙉" } } }
-
9:48 - CatServiceClient openapi-generator-config.yaml
generate: - types - client
-
13:24 - Adapting as the API evolves
import SwiftUI import OpenAPIRuntime import OpenAPIURLSession #Preview { ContentView() } struct ContentView: View { @State private var emoji = "🫥" var body: some View { VStack { Text(emoji).font(.system(size: 100)) Button("Get cat!") { Task { try? await updateEmoji() } } Button("More cats!") { Task { try? await updateEmoji(count: 3) } } } .padding() .buttonStyle(.borderedProminent) } let client: Client init() { self.client = Client( serverURL: try! Servers.server1(), transport: URLSessionTransport() ) } func updateEmoji(count: Int = 1) async throws { let response = try await client.getEmoji(Operations.getEmoji.Input( query: Operations.getEmoji.Input.Query(count: count) )) switch response { case let .ok(okResponse): switch okResponse.body { case .text(let text): emoji = text } case .undocumented(statusCode: let statusCode, _): print("cat-astrophe: \(statusCode)") emoji = "🙉" } } }
-
15:06 - Testing your app with mocks
import SwiftUI import OpenAPIRuntime import OpenAPIURLSession #Preview { ContentView(client: MockClient()) } struct ContentView<C: APIProtocol>: View { @State private var emoji = "🫥" var body: some View { VStack { Text(emoji).font(.system(size: 100)) Button("Get cat!") { Task { try? await updateEmoji() } } Button("More cats!") { Task { try? await updateEmoji(count: 3) } } } .padding() .buttonStyle(.borderedProminent) } let client: C init(client: C) { self.client = client } init() where C == Client { self.client = Client( serverURL: try! Servers.server1(), transport: URLSessionTransport() ) } func updateEmoji(count: Int = 1) async throws { let response = try await client.getEmoji(Operations.getEmoji.Input( query: Operations.getEmoji.Input.Query(count: count) )) switch response { case let .ok(okResponse): switch okResponse.body { case .text(let text): emoji = text } case .undocumented(statusCode: let statusCode, _): print("cat-astrophe: \(statusCode)") emoji = "🙉" } } } struct MockClient: APIProtocol { func getEmoji(_ input: Operations.getEmoji.Input) async throws -> Operations.getEmoji.Output { let count = input.query.count ?? 1 let emojis = String(repeating: "🤖", count: count) return .ok(Operations.getEmoji.Output.Ok( body: .text(emojis) )) } }
-
16:58 - Implementing a backend server
import Foundation import OpenAPIRuntime import OpenAPIVapor import Vapor struct Handler: APIProtocol { func getEmoji(_ input: Operations.getEmoji.Input) async throws -> Operations.getEmoji.Output { let candidates = "🐱😹😻🙀😿😽😸😺😾😼" let chosen = String(candidates.randomElement()!) let count = input.query.count ?? 1 let emojis = String(repeating: chosen, count: count) return .ok(Operations.getEmoji.Output.Ok(body: .text(emojis))) } } @main struct CatService { public static func main() throws { let app = Vapor.Application() let transport = VaporTransport(routesBuilder: app) let handler = Handler() try handler.registerHandlers(on: transport, serverURL: Servers.server1()) try app.run() } }
-
18:43 - CatService Package.swift
// swift-tools-version: 5.8 import PackageDescription let package = Package( name: "CatService", platforms: [ .macOS(.v13), ], dependencies: [ .package( url: "https://github.com/apple/swift-openapi-generator", .upToNextMinor(from: "0.1.0") ), .package( url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.1.0") ), .package( url: "https://github.com/swift-server/swift-openapi-vapor", .upToNextMinor(from: "0.1.0") ), .package( url: "https://github.com/vapor/vapor", .upToNextMajor(from: "4.69.2") ), ], targets: [ .executableTarget( name: "CatService", dependencies: [ .product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"), .product(name: "OpenAPIVapor", package: "swift-openapi-vapor"), .product(name: "Vapor", package: "vapor"), ], resources: [ .process("Resources/cat.mp4"), ], plugins: [ .plugin(name: "OpenAPIGenerator", package: "swift-openapi-generator"), ] ), ] )
-
19:08 - CatService openapi.yaml
openapi: "3.0.3" info: title: CatService version: 1.0.0 servers: - url: http://localhost:8080/api description: "Localhost cats 🙀" paths: /emoji: get: operationId: getEmoji parameters: - name: count required: false in: query description: "The number of cats to return. 😽😽😽" schema: type: integer responses: '200': description: "Returns a random emoji, of a cat, ofc! 😻" content: text/plain: schema: type: string
-
19:10 - CatService openapi-generator-config.yaml
generate: - types - server
-
20:11 - Adding an operation to the OpenAPI document
openapi: "3.0.3" info: title: CatService version: 1.0.0 servers: - url: http://localhost:8080/api description: "Localhost cats 🙀" paths: /emoji: get: operationId: getEmoji parameters: - name: count required: false in: query description: "The number of cats to return. 😽😽😽" schema: type: integer responses: '200': description: "Returns a random emoji, of a cat, ofc! 😻" content: text/plain: schema: type: string /clip: get: operationId: getClip responses: '200': description: "Returns a cat video! 😽" content: video/mp4: schema: type: string format: binary
-
20:26 - Adding a new API operation
import Foundation import OpenAPIRuntime import OpenAPIVapor import Vapor struct Handler: APIProtocol { func getClip(_ input: Operations.getClip.Input) async throws -> Operations.getClip.Output { let clipResourceURL = Bundle.module.url(forResource: "cat", withExtension: "mp4")! let clipData = try Data(contentsOf: clipResourceURL) return .ok(Operations.getClip.Output.Ok(body: .binary(clipData))) } func getEmoji(_ input: Operations.getEmoji.Input) async throws -> Operations.getEmoji.Output { let candidates = "🐱😹😻🙀😿😽😸😺😾😼" let chosen = String(candidates.randomElement()!) let count = input.query.count ?? 1 let emojis = String(repeating: chosen, count: count) return .ok(Operations.getEmoji.Output.Ok(body: .text(emojis))) } } @main struct CatService { public static func main() throws { let app = Vapor.Application() let transport = VaporTransport(routesBuilder: app) let handler = Handler() try handler.registerHandlers(on: transport, serverURL: Servers.server1()) try app.run() } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。