RESTful APIの作成
Ktorを使用したKotlinでのRESTful APIの作成方法
コード例: tutorial-server-restful-api
使用されているプラグイン:
このチュートリアルでは、KotlinとKtorを使用してバックエンドサービスを構築する方法を説明し、JSONデータを生成するRESTful APIの例を紹介します。
以下の内容を学習します:
- JSONシリアライズを使用するRESTfulサービスを作成する。
- Content Negotiationのプロセスを理解する。ContentNegotiationプラグインは、クライアントとサーバー間のメディアタイプのネゴシエーションと、特定のフォーマットでのコンテンツのシリアライズ/デシリアライズという2つの主要な目的を果たします。
- Ktor内でREST APIのルートを定義する。
前提条件
このチュートリアルは単独で行うこともできますが、
IntelliJ IDEAのインストールをお勧めしますが、お好みの他のIDEを使用することもできます。
RESTfulタスクマネージャーの作成
このチュートリアルでは、既存のタスクマネージャーをRESTfulサービスとして書き直します。これを行うために、いくつかのKtor
既存のプロジェクトに手動で追加することもできますが、新しいプロジェクトを生成してから、前のチュートリアルのコードを段階的に追加していく方が簡単です。進めながらすべてのコードを反復するため、前のプロジェクトを手元に用意しておく必要はありません。
Ktor Project Generatorにアクセスします。
Project artifactフィールドに、プロジェクト名として com.example.ktor-rest-task-app と入力します。

プラグインセクションで、以下のプラグインを検索し、Addボタンをクリックして追加します:
- Content Negotiation
- kotlinx.serialization
- Static Content
プラグインを追加すると、プロジェクト設定の下にすべてのプラグインがリストされます。 
Downloadボタンをクリックして、Ktorプロジェクトを生成しダウンロードします。
IntelliJ IDEAでKtorプロジェクトを開き、探索し、実行するチュートリアルで説明したように、IntelliJ IDEAでプロジェクトを開きます。
src/main/kotlinに移動し、 modelというサブパッケージを作成します。
modelパッケージの中に、新しい Task.ktファイルを作成します。
Task.ktファイルを開き、優先度を表す
enumとタスクを表すclassを追加します:kotlin前のチュートリアルでは、拡張関数を使用して
TaskをHTMLに変換しました。今回は、Taskクラスにkotlinx.serializationライブラリのSerializable型のアノテーションを付けています。Routing.ktファイルを開き、既存のコードを以下の実装に置き換えます:
kotlin前のチュートリアルと同様に、URL
/tasksへのGETリクエストのルートを作成しました。今回は、タスクのリストを手動で変換する代わりに、リストをそのまま返しています。IntelliJ IDEAで、実行ボタン(
)をクリックしてアプリケーションを起動します。
ブラウザで http://0.0.0.0:8080/tasks にアクセスします。以下のように、タスクリストのJSON版が表示されるはずです:

明らかに、私たちの代わりに多くの処理が行われています。具体的には何が起きているのでしょうか?
コンテンツネゴシエーションを理解する
ブラウザ経由のコンテンツネゴシエーション
プロジェクトを作成した際、
HTTPでは、クライアントは Accept ヘッダーを通じてレンダリング可能なコンテンツタイプを通知します。このヘッダーの値は1つ以上のコンテンツタイプです。上記の場合、ブラウザに組み込まれている開発ツールを使用して、このヘッダーの値を確認できます。
以下の例を考えてみましょう:
*/* が含まれていることに注目してください。このヘッダーは、HTML、XML、または画像を受け入れることを示していますが、他のあらゆるコンテンツタイプも受け入れることを意味します。
Content Negotiationプラグインは、データをブラウザに送り返すためのフォーマットを見つける必要があります。プロジェクト内の生成されたコードを見ると、src/main/kotlin 内に Serialization.kt というファイルがあり、以下の内容が含まれています:
このコードは ContentNegotiation プラグインをインストールし、kotlinx.serialization プラグインも構成します。これにより、クライアントがリクエストを送信すると、サーバーはJSONとしてシリアライズされたオブジェクトを返送できます。
ブラウザからのリクエストの場合、ContentNegotiation プラグインはJSONしか返せないことを認識しており、ブラウザは送られてきたものを何でも表示しようとします。そのため、リクエストは成功します。
これをシミュレートするために、src/main/resources/static 内の index.html ページを開き、デフォルトのコンテンツを以下に置き換えます:
htmlこのページにはHTMLフォームと空のテーブルが含まれています。フォームを送信すると、JavaScriptイベントハンドラーが
Acceptヘッダーをapplication/jsonに設定して/tasksエンドポイントにリクエストを送信します。返されたデータはデシリアライズされ、HTMLテーブルに追加されます。IntelliJ IDEAで、再実行ボタン(
)をクリックしてアプリケーションを再起動します。
URL http://0.0.0.0:8080/static/index.html にアクセスします。View The Tasks ボタンをクリックしてデータを取得できるはずです:

本番環境では、通常JSONをブラウザに直接表示することはありません。代わりに、ブラウザで実行されているJavaScriptコードがリクエストを行い、返されたデータをシングルページアプリケーション(SPA)の一部として表示します。通常、この種のアプリケーションは React、Angular、または Vue.js のようなフレームワークを使用して記述されます。
GETルートの追加
コンテンツネゴシエーションのプロセスに慣れたところで、
タスクリポジトリの再利用
タスクのリポジトリは変更なしで再利用できるので、まずそれを行いましょう。
modelパッケージ内に、新しい TaskRepository.kt ファイルを作成します。
TaskRepository.kt を開き、以下のコードを追加します:
kotlin
GETリクエスト用のルートを再利用する
リポジトリを作成したので、GETリクエスト用のルートを実装できます。タスクをHTMLに変換することを心配する必要がなくなったため、以前のコードを簡略化できます:
src/main/kotlin 内の Routing.kt ファイルに移動します。
Application.configureRouting()関数内の/tasksルートのコードを、以下の実装に更新します:kotlinこれにより、サーバーは以下のGETリクエストに応答できるようになります:
/tasksはリポジトリ内のすべてのタスクを返します。/tasks/byName/{taskName}は指定されたtaskNameでフィルタリングされたタスクを返します。/tasks/byPriority/{priority}は指定されたpriorityでフィルタリングされたタスクを返します。
IntelliJ IDEAで、再実行ボタン(
)をクリックしてアプリケーションを再起動します。
機能のテスト
ブラウザでこれらのルートをテストできます。例えば、http://0.0.0.0:8080/tasks/byPriority/Medium にアクセスすると、Medium 優先度のすべてのタスクがJSON形式で表示されます:

この種のリクエストは通常JavaScriptから行われるため、より詳細なテストが好ましいです。このために、Postmanのような専門的なツールを使用できます。
Postmanで、URL
http://0.0.0.0:8080/tasks/byPriority/Mediumを使用して新しいGETリクエストを作成します。Headersペインで、Acceptヘッダーの値を
application/jsonに設定します。Sendをクリックしてリクエストを送信し、レスポンスビューアーでレスポンスを確認します。

プロジェクトのルートディレクトリに、新しい REST Task Manager.http ファイルを作成します。
REST Task Manager.http ファイルを開き、以下のGETリクエストを追加します:
httpIntelliJ IDEA内でリクエストを送信するには、その横にあるガターアイコン(
)をクリックします。
これにより、Servicesツールウィンドウで実行されます:

IntelliJ IDEA Ultimateでは、HTTPリクエストファイルで同じ手順を実行できます。
NOTE
ルートをテストする別の方法として、Kotlin Notebook内から khttp ライブラリを使用することもできます。POSTリクエスト用のルートを追加する
前のチュートリアルでは、タスクはHTMLフォームを通じて作成されました。しかし、現在はRESTfulサービスを構築しているため、その必要はありません。代わりに、主要な処理を肩代わりしてくれる kotlinx.serialization フレームワークを活用します。
src/main/kotlin 内の Routing.kt ファイルを開きます。
以下のように、新しいPOSTルートを
Application.configureRouting()関数に追加します:kotlin以下の新しいインポートを追加します:
kotlinPOSTリクエストが
/tasksに送信されると、kotlinx.serializationフレームワークがリクエストのボディをTaskオブジェクトに変換します。これが成功すると、タスクがリポジトリに追加されます。デシリアライズプロセスが失敗した場合、サーバーはSerializationExceptionを処理し、タスクが重複している場合はIllegalStateExceptionを処理します。アプリケーションを再起動します。
Postmanでこの機能をテストするには、URL
http://0.0.0.0:8080/tasksに対して新しいPOSTリクエストを作成します。Bodyパネルで、新しいタスクを表す以下のJSONドキュメントを追加します:
json
Sendをクリックしてリクエストを送信します。
http://0.0.0.0:8080/tasks にGETリクエストを送信することで、タスクが追加されたことを確認できます。
IntelliJ IDEA Ultimate内では、HTTPリクエストファイルに以下を追加することで同じ手順を実行できます:
http
削除機能のサポート追加
サービスの基本操作の追加はほぼ完了しました。これらはCRUD(Create, Read, Update, and Delete)操作としてよくまとめられます。ここでは削除操作を実装します。
TaskRepository.kt ファイルの
TaskRepositoryオブジェクト内に、名前を基にタスクを削除する以下のメソッドを追加します:kotlinRouting.kt ファイルを開き、DELETEリクエストを処理するエンドポイントを
routing()関数に追加します:kotlinアプリケーションを再起動します。
HTTPリクエストファイルに以下のDELETEリクエストを追加します:
httpIntelliJ IDEA内でDELETEリクエストを送信するには、その横にあるガターアイコン(
)をクリックします。
Servicesツールウィンドウにレスポンスが表示されます:

Ktor Clientを使用したユニットテストの作成
これまでは手動でアプリケーションをテストしてきましたが、すでにお気づきの通り、このアプローチは時間がかかり、規模の拡大に対応できません。代わりに、組み込みの client オブジェクトを使用してJSONの取得とデシリアライズを行う
src/test/kotlin 内の ServerTest.kt ファイルを開きます。
ServerTest.kt ファイルの内容を以下に置き換えます:
kotlinサーバーで行ったのと同様に、プラグインに
ContentNegotiationとkotlinx.serializationプラグインをインストールする必要があることに注意してください。build.gradle.kts ファイルに以下の依存関係を追加します:
kotlin
JsonPathを使用したユニットテストの作成
Ktor Clientや同様のライブラリを使用してサービスをテストするのは便利ですが、品質保証(QA)の観点からは欠点があります。サーバーがJSONを直接処理しない場合、JSONの構造に関する想定が正しいかどうか確信が持てないためです。
例えば、以下のような想定です:
- 実際には
objectが使用されているのに、値がarrayに格納されている。 - プロパティが
stringsなのに、numbersとして格納されている。 - メンバーが宣言順にシリアライズされるはずが、そうなっていない。
サービスが複数のクライアントによって使用されることを目的としている場合、JSON構造に自信を持つことが不可欠です。これを実現するには、Ktor Clientを使用してサーバーからテキストを取得し、JSONPath ライブラリを使用してこのコンテンツを分析します。
build.gradle.kts ファイルの
dependenciesブロックに JSONPath ライブラリを追加します:kotlinsrc/test/kotlin フォルダに移動し、新しい ApplicationJsonPathTest.kt ファイルを作成します。
ApplicationJsonPathTest.kt ファイルを開き、以下の内容を追加します:
kotlinJsonPath クエリは以下のように機能します:
$[*].nameは「ドキュメントを配列として扱い、各エントリの name プロパティの値を返す」ことを意味します。$[?(@.priority == '$priority')].nameは「指定された値と等しい優先度を持つ配列内のすべてのエントリの name プロパティの値を返す」ことを意味します。
このようなクエリを使用して、返されたJSONに対する理解を確認できます。コードのリファクタリングやサービスの再デプロイを行う際、現在のフレームワークでのデシリアライズを妨げない変更であっても、シリアライズにおけるあらゆる修正が特定されます。これにより、自信を持って公開APIを再公開できます。
次のステップ
おめでとうございます!タスクマネージャーアプリケーションのRESTful APIサービスの作成を完了し、Ktor ClientとJsonPathを使用したユニットテストの要点を学びました。
