Skip to content

RESTful APIの作成

Ktorを使用したKotlinでのRESTful APIの作成方法

コード例: tutorial-server-restful-api

使用されているプラグイン:

Routing
Routingは、サーバーアプリケーションで受信リクエストを処理するためのコアプラグインです。
,
Static Content
スタイルシート、スクリプト、画像などの静的コンテンツを提供する方法を学びます。
,
Content Negotiation
ContentNegotiationプラグインは、クライアントとサーバー間のメディアタイプのネゴシエーションと、特定のフォーマットでのコンテンツのシリアライズ/デシリアライズという2つの主要な目的を果たします。
, kotlinx.serialization

このチュートリアルでは、KotlinとKtorを使用してバックエンドサービスを構築する方法を説明し、JSONデータを生成するRESTful APIの例を紹介します。

前のチュートリアル
タスクマネージャーアプリケーションの構築を通じて、KotlinとKtorでのルーティング、リクエストの処理、およびパラメータの基本を学びます。
では、バリデーション、エラー処理、およびユニットテストの基礎を紹介しました。このチュートリアルでは、これらのトピックを拡張し、タスクを管理するためのRESTfulサービスを作成します。

以下の内容を学習します:

  • JSONシリアライズを使用するRESTfulサービスを作成する。
  • Content Negotiation
    ContentNegotiationプラグインは、クライアントとサーバー間のメディアタイプのネゴシエーションと、特定のフォーマットでのコンテンツのシリアライズ/デシリアライズという2つの主要な目的を果たします。
    のプロセスを理解する。
  • Ktor内でREST APIのルートを定義する。

前提条件

このチュートリアルは単独で行うこともできますが、

リクエストの処理とレスポンスの生成方法
タスクマネージャーアプリケーションの構築を通じて、KotlinとKtorでのルーティング、リクエストの処理、およびパラメータの基本を学びます。
を学ぶために、前のチュートリアルを完了することを強くお勧めします。

IntelliJ IDEAのインストールをお勧めしますが、お好みの他のIDEを使用することもできます。

RESTfulタスクマネージャーの作成

このチュートリアルでは、既存のタスクマネージャーをRESTfulサービスとして書き直します。これを行うために、いくつかのKtor

プラグイン
プラグインは、シリアライズ、コンテンツエンコーディング、圧縮などの共通機能を提供します。
を使用します。

既存のプロジェクトに手動で追加することもできますが、新しいプロジェクトを生成してから、前のチュートリアルのコードを段階的に追加していく方が簡単です。進めながらすべてのコードを反復するため、前のプロジェクトを手元に用意しておく必要はありません。

  1. Ktor Project Generatorにアクセスします。

  2. Project artifactフィールドに、プロジェクト名として com.example.ktor-rest-task-app と入力します。 Ktor Project Generatorでプロジェクトのアーティファクトを指定する

  3. プラグインセクションで、以下のプラグインを検索し、Addボタンをクリックして追加します:

    1. Content Negotiation
    2. kotlinx.serialization
    3. Static Content

    Ktor Project Generatorでプラグインを追加する プラグインを追加すると、プロジェクト設定の下にすべてのプラグインがリストされます。 Ktor Project Generatorのプラグインリスト

  4. Downloadボタンをクリックして、Ktorプロジェクトを生成しダウンロードします。

  1. IntelliJ IDEAでKtorプロジェクトを開き、探索し、実行するチュートリアルで説明したように、IntelliJ IDEAでプロジェクトを開きます。

  2. src/main/kotlinに移動し、 modelというサブパッケージを作成します。

  3. modelパッケージの中に、新しい Task.ktファイルを作成します。

  4. Task.ktファイルを開き、優先度を表すenumとタスクを表すclassを追加します:

    kotlin

    前のチュートリアルでは、拡張関数を使用してTaskをHTMLに変換しました。今回は、Taskクラスにkotlinx.serializationライブラリのSerializable型のアノテーションを付けています。

  5. Routing.ktファイルを開き、既存のコードを以下の実装に置き換えます:

    kotlin

    前のチュートリアルと同様に、URL /tasks へのGETリクエストのルートを作成しました。今回は、タスクのリストを手動で変換する代わりに、リストをそのまま返しています。

  6. IntelliJ IDEAで、実行ボタン(IntelliJ IDEAの実行アイコン)をクリックしてアプリケーションを起動します。

  7. ブラウザで http://0.0.0.0:8080/tasks にアクセスします。以下のように、タスクリストのJSON版が表示されるはずです:

  8. ブラウザ画面に表示されたJSONデータ

    明らかに、私たちの代わりに多くの処理が行われています。具体的には何が起きているのでしょうか?

コンテンツネゴシエーションを理解する

ブラウザ経由のコンテンツネゴシエーション

プロジェクトを作成した際、

Content Negotiation
ContentNegotiationプラグインは、クライアントとサーバー間のメディアタイプのネゴシエーションと、特定のフォーマットでのコンテンツのシリアライズ/デシリアライズという2つの主要な目的を果たします。
プラグインを含めました。このプラグインは、クライアントがレンダリングできるコンテンツの種類を確認し、現在のサービスが提供できるコンテンツタイプと照合します。そのため、Content Negotiation(コンテンツネゴシエーション)という用語が使われます。

HTTPでは、クライアントは Accept ヘッダーを通じてレンダリング可能なコンテンツタイプを通知します。このヘッダーの値は1つ以上のコンテンツタイプです。上記の場合、ブラウザに組み込まれている開発ツールを使用して、このヘッダーの値を確認できます。

以下の例を考えてみましょう:

*/* が含まれていることに注目してください。このヘッダーは、HTML、XML、または画像を受け入れることを示していますが、他のあらゆるコンテンツタイプも受け入れることを意味します。

Content Negotiationプラグインは、データをブラウザに送り返すためのフォーマットを見つける必要があります。プロジェクト内の生成されたコードを見ると、src/main/kotlin 内に Serialization.kt というファイルがあり、以下の内容が含まれています:

kotlin

このコードは ContentNegotiation プラグインをインストールし、kotlinx.serialization プラグインも構成します。これにより、クライアントがリクエストを送信すると、サーバーはJSONとしてシリアライズされたオブジェクトを返送できます。

ブラウザからのリクエストの場合、ContentNegotiation プラグインはJSONしか返せないことを認識しており、ブラウザは送られてきたものを何でも表示しようとします。そのため、リクエストは成功します。

    本番環境では、通常JSONをブラウザに直接表示することはありません。代わりに、ブラウザで実行されているJavaScriptコードがリクエストを行い、返されたデータをシングルページアプリケーション(SPA)の一部として表示します。通常、この種のアプリケーションは ReactAngular、または Vue.js のようなフレームワークを使用して記述されます。

  1. これをシミュレートするために、src/main/resources/static 内の index.html ページを開き、デフォルトのコンテンツを以下に置き換えます:

    html

    このページにはHTMLフォームと空のテーブルが含まれています。フォームを送信すると、JavaScriptイベントハンドラーが Accept ヘッダーを application/json に設定して /tasks エンドポイントにリクエストを送信します。返されたデータはデシリアライズされ、HTMLテーブルに追加されます。

  2. IntelliJ IDEAで、再実行ボタン(IntelliJ IDEAの再実行アイコン)をクリックしてアプリケーションを再起動します。

  3. URL http://0.0.0.0:8080/static/index.html にアクセスします。View The Tasks ボタンをクリックしてデータを取得できるはずです:

    ボタンとHTMLテーブルとして表示されたタスクが表示されているブラウザウィンドウ

GETルートの追加

コンテンツネゴシエーションのプロセスに慣れたところで、

前のチュートリアル
タスクマネージャーアプリケーションの構築を通じて、KotlinとKtorでのルーティング、リクエストの処理、およびパラメータの基本を学びます。
の機能をこちらに移植していきましょう。

タスクリポジトリの再利用

タスクのリポジトリは変更なしで再利用できるので、まずそれを行いましょう。

  1. modelパッケージ内に、新しい TaskRepository.kt ファイルを作成します。

  2. TaskRepository.kt を開き、以下のコードを追加します:

    kotlin

GETリクエスト用のルートを再利用する

リポジトリを作成したので、GETリクエスト用のルートを実装できます。タスクをHTMLに変換することを心配する必要がなくなったため、以前のコードを簡略化できます:

  1. src/main/kotlin 内の Routing.kt ファイルに移動します。

  2. Application.configureRouting() 関数内の /tasks ルートのコードを、以下の実装に更新します:

    kotlin

    これにより、サーバーは以下のGETリクエストに応答できるようになります:

    • /tasks はリポジトリ内のすべてのタスクを返します。
    • /tasks/byName/{taskName} は指定された taskName でフィルタリングされたタスクを返します。
    • /tasks/byPriority/{priority} は指定された priority でフィルタリングされたタスクを返します。
  3. IntelliJ IDEAで、再実行ボタン(IntelliJ IDEAの再実行アイコン)をクリックしてアプリケーションを再起動します。

機能のテスト

    ブラウザでこれらのルートをテストできます。例えば、http://0.0.0.0:8080/tasks/byPriority/Medium にアクセスすると、Medium 優先度のすべてのタスクがJSON形式で表示されます:

    Medium優先度のタスクがJSON形式で表示されているブラウザウィンドウ

    この種のリクエストは通常JavaScriptから行われるため、より詳細なテストが好ましいです。このために、Postmanのような専門的なツールを使用できます。

  1. Postmanで、URL http://0.0.0.0:8080/tasks/byPriority/Medium を使用して新しいGETリクエストを作成します。

  2. Headersペインで、Acceptヘッダーの値を application/json に設定します。

  3. Sendをクリックしてリクエストを送信し、レスポンスビューアーでレスポンスを確認します。

    Medium優先度のタスクをJSON形式で表示しているPostmanのGETリクエスト

    IntelliJ IDEA Ultimateでは、HTTPリクエストファイルで同じ手順を実行できます。

  1. プロジェクトのルートディレクトリに、新しい REST Task Manager.http ファイルを作成します。

  2. REST Task Manager.http ファイルを開き、以下のGETリクエストを追加します:

    http
  3. IntelliJ IDEA内でリクエストを送信するには、その横にあるガターアイコン(IntelliJ IDEAのガターアイコン)をクリックします。

  4. これにより、Servicesツールウィンドウで実行されます:

    Medium優先度のタスクをJSON形式で表示しているHTTPファイル内のGETリクエスト

NOTE

ルートをテストする別の方法として、Kotlin Notebook内から khttp ライブラリを使用することもできます。

POSTリクエスト用のルートを追加する

前のチュートリアルでは、タスクはHTMLフォームを通じて作成されました。しかし、現在はRESTfulサービスを構築しているため、その必要はありません。代わりに、主要な処理を肩代わりしてくれる kotlinx.serialization フレームワークを活用します。

  1. src/main/kotlin 内の Routing.kt ファイルを開きます。

  2. 以下のように、新しいPOSTルートを Application.configureRouting() 関数に追加します:

    kotlin

    以下の新しいインポートを追加します:

    kotlin

    POSTリクエストが /tasks に送信されると、kotlinx.serialization フレームワークがリクエストのボディを Task オブジェクトに変換します。これが成功すると、タスクがリポジトリに追加されます。デシリアライズプロセスが失敗した場合、サーバーは SerializationException を処理し、タスクが重複している場合は IllegalStateException を処理します。

  3. アプリケーションを再起動します。

  4. Postmanでこの機能をテストするには、URL http://0.0.0.0:8080/tasks に対して新しいPOSTリクエストを作成します。

  5. Bodyパネルで、新しいタスクを表す以下のJSONドキュメントを追加します:

    json
    新しいタスクを追加するためのPostmanのPOSTリクエスト
  6. Sendをクリックしてリクエストを送信します。

  7. http://0.0.0.0:8080/tasks にGETリクエストを送信することで、タスクが追加されたことを確認できます。

  8. IntelliJ IDEA Ultimate内では、HTTPリクエストファイルに以下を追加することで同じ手順を実行できます:

    http

削除機能のサポート追加

サービスの基本操作の追加はほぼ完了しました。これらはCRUD(Create, Read, Update, and Delete)操作としてよくまとめられます。ここでは削除操作を実装します。

  1. TaskRepository.kt ファイルの TaskRepository オブジェクト内に、名前を基にタスクを削除する以下のメソッドを追加します:

    kotlin
  2. Routing.kt ファイルを開き、DELETEリクエストを処理するエンドポイントを routing() 関数に追加します:

    kotlin
  3. アプリケーションを再起動します。

  4. HTTPリクエストファイルに以下のDELETEリクエストを追加します:

    http
  5. IntelliJ IDEA内でDELETEリクエストを送信するには、その横にあるガターアイコン(IntelliJ IDEAのガターアイコン)をクリックします。

  6. Servicesツールウィンドウにレスポンスが表示されます:

    HTTPリクエストファイル内のDELETEリクエスト

Ktor Clientを使用したユニットテストの作成

これまでは手動でアプリケーションをテストしてきましたが、すでにお気づきの通り、このアプローチは時間がかかり、規模の拡大に対応できません。代わりに、組み込みの client オブジェクトを使用してJSONの取得とデシリアライズを行う

JUnitテスト
特別なテスティングエンジンを使用してサーバーアプリケーションをテストする方法を学びます。
を実装できます。

  1. src/test/kotlin 内の ServerTest.kt ファイルを開きます。

  2. ServerTest.kt ファイルの内容を以下に置き換えます:

    kotlin

    サーバーで行ったのと同様に、プラグインContentNegotiationkotlinx.serialization プラグインをインストールする必要があることに注意してください。

  3. build.gradle.kts ファイルに以下の依存関係を追加します:

    kotlin

JsonPathを使用したユニットテストの作成

Ktor Clientや同様のライブラリを使用してサービスをテストするのは便利ですが、品質保証(QA)の観点からは欠点があります。サーバーがJSONを直接処理しない場合、JSONの構造に関する想定が正しいかどうか確信が持てないためです。

例えば、以下のような想定です:

  • 実際には object が使用されているのに、値が array に格納されている。
  • プロパティが strings なのに、numbers として格納されている。
  • メンバーが宣言順にシリアライズされるはずが、そうなっていない。

サービスが複数のクライアントによって使用されることを目的としている場合、JSON構造に自信を持つことが不可欠です。これを実現するには、Ktor Clientを使用してサーバーからテキストを取得し、JSONPath ライブラリを使用してこのコンテンツを分析します。

  1. build.gradle.kts ファイルの dependencies ブロックに JSONPath ライブラリを追加します:

    kotlin
  2. src/test/kotlin フォルダに移動し、新しい ApplicationJsonPathTest.kt ファイルを作成します。

  3. ApplicationJsonPathTest.kt ファイルを開き、以下の内容を追加します:

    kotlin

    JsonPath クエリは以下のように機能します:

    • $[*].name は「ドキュメントを配列として扱い、各エントリの name プロパティの値を返す」ことを意味します。
    • $[?(@.priority == '$priority')].name は「指定された値と等しい優先度を持つ配列内のすべてのエントリの name プロパティの値を返す」ことを意味します。

    このようなクエリを使用して、返されたJSONに対する理解を確認できます。コードのリファクタリングやサービスの再デプロイを行う際、現在のフレームワークでのデシリアライズを妨げない変更であっても、シリアライズにおけるあらゆる修正が特定されます。これにより、自信を持って公開APIを再公開できます。

次のステップ

おめでとうございます!タスクマネージャーアプリケーションのRESTful APIサービスの作成を完了し、Ktor ClientとJsonPathを使用したユニットテストの要点を学びました。

次のチュートリアル
KotlinとKtor、およびThymeleafテンプレートを使用してウェブサイトを構築する方法を学びます。
に進んで、APIサービスを再利用してウェブアプリケーションを構築する方法を学びましょう。