型安全なルーティング
必要な依存関係: io.ktor:ktor-server-resources
コード例: resource-routing
Ktorは、型安全なルーティングを実装できるResourcesプラグインを提供しています。これを実現するには、型付きルートとして機能するクラスを作成し、そのクラスに@Resourceキーワードを使用してアノテーションを付加する必要があります。@Resourceアノテーションは、kotlinx.serializationライブラリによって提供される@Serializableの振る舞いを持つことに注意してください。
Ktorクライアントは、サーバーに対して型付きリクエストを行う機能を提供しています。
依存関係の追加
kotlinx.serializationの追加
リソースクラスは@Serializableの振る舞いを持つ必要があるため、Setupセクションの記述に従ってKotlin serializationプラグインを追加する必要があります。
Resources 依存関係の追加
Resourcesを使用するには、ビルドスクリプトにktor-server-resourcesアーティファクトを含める必要があります。
Resources のインストール
アプリケーションにResourcesプラグインをインストールするには、 指定された
install関数に渡します。 以下のコードスニペットは、Resourcesをインストールする方法を示しています。 - ...
embeddedServer関数の呼び出し内。 - ...
Applicationクラスの拡張関数である、明示的に定義されたmodule内。
リソースクラスの作成
各リソースクラスには@Resourceアノテーションを付ける必要があります。 以下では、単一のパスセグメントの定義、クエリおよびパスパラメーターなど、リソースクラスのいくつかの例を見ていきます。
リソースURL
以下の例は、/articlesパスに応答するリソースを指定するArticlesクラスを定義する方法を示しています。
import io.ktor.resources.*
@Resource("/articles")
class Articles()クエリパラメーターを持つリソース
以下のArticlesクラスは、クエリパラメーターとして機能するsort文字列プロパティを持っており、/articles?sort=newのようなsortクエリパラメーターを持つパスに応答するリソースを定義できます。
@Resource("/articles")
class Articles(val sort: String? = "new")ネストされたクラスを持つリソース
クラスをネストして、複数のパスセグメントを含むリソースを作成できます。この場合、ネストされたクラスは外部クラスの型のプロパティを持つ必要があることに注意してください。 以下の例は、/articles/newパスに応答するリソースを示しています。
@Resource("/articles")
class Articles() {
@Resource("new")
class New(val parent: Articles = Articles())
}パスパラメーターを持つリソース
以下の例は、パスセグメントに一致し、idという名前のパラメーターとしてキャプチャする、ネストされた {id} 整数パスパラメーターを追加する方法を示しています。
@Resource("/articles")
class Articles() {
@Resource("{id}")
class Id(val parent: Articles = Articles(), val id: Long)
}例として、このリソースは/articles/12への応答に使用できます。
例: CRUD操作用のリソース
上記の例をまとめて、CRUD操作用のArticlesリソースを作成してみましょう。
@Resource("/articles")
class Articles(val sort: String? = "new") {
@Resource("new")
class New(val parent: Articles = Articles())
@Resource("{id}")
class Id(val parent: Articles = Articles(), val id: Long) {
@Resource("edit")
class Edit(val parent: Id)
}
}このリソースは、すべての記事の一覧表示、新しい記事の投稿、編集などに使用できます。このリソースに対するルートハンドラーの定義については、次の章で説明します。
完全な例はこちらにあります: resource-routing
ルートハンドラーの定義
型付きリソースのルートハンドラーを定義するには、動詞関数(get、post、putなど)にリソースクラスを渡す必要があります。 例えば、以下のルートハンドラーは/articlesパスに応答します。
@Resource("/articles")
class Articles()
fun Application.module() {
install(Resources)
routing {
get<Articles> { articles ->
// すべての記事を取得 ...
call.respondText("List of articles: $articles")
}
}
}以下の例は、例: CRUD操作用のリソースで作成したArticlesリソースのルートハンドラーを定義する方法を示しています。ルートハンドラー内では、Articleをパラメーターとしてアクセスし、そのプロパティ値を取得できることに注意してください。
fun Application.module() {
install(Resources)
routing {
get<Articles> { article ->
// すべての記事を取得 ...
call.respondText("List of articles sorted starting from ${article.sort}")
}
get<Articles.New> {
// 新しい記事を作成するためのフィールドを持つページを表示 ...
call.respondText("Create a new article")
}
post<Articles> {
// 記事を保存 ...
call.respondText("An article is saved", status = HttpStatusCode.Created)
}
get<Articles.Id> { article ->
// ID ${article.id} の記事を表示 ...
call.respondText("An article with id ${article.id}", status = HttpStatusCode.OK)
}
get<Articles.Id.Edit> { article ->
// 記事を編集するためのフィールドを持つページを表示 ...
call.respondText("Edit an article with id ${article.parent.id}", status = HttpStatusCode.OK)
}
put<Articles.Id> { article ->
// 記事を更新 ...
call.respondText("An article with id ${article.id} updated", status = HttpStatusCode.OK)
}
delete<Articles.Id> { article ->
// 記事を削除 ...
call.respondText("An article with id ${article.id} deleted", status = HttpStatusCode.OK)
}
}
}各エンドポイントのリクエスト処理に関するいくつかのヒントを以下に示します。
get<Articles>このルートハンドラーは、
sortクエリパラメーターに従ってソートされたすべての記事を返すことが想定されています。 例えば、これはすべての記事を含むHTMLページやJSONオブジェクトになります。get<Articles.New>このエンドポイントは、新しい記事を作成するためのフィールドを含むウェブフォームで応答します。
post<Articles>post<Articles>エンドポイントは、ウェブフォームを使用して送信されたパラメーターを受信することが想定されています。 Ktorでは、ContentNegotiationプラグインを使用してJSONデータをオブジェクトとして受信することもできます。get<Articles.Id>このルートハンドラーは、指定された識別子を持つ記事を返すことが想定されています。 これは、記事を表示するHTMLページや、記事データを含むJSONオブジェクトになります。
get<Articles.Id.Edit>このエンドポイントは、既存の記事を編集するためのフィールドを含むウェブフォームで応答します。
put<Articles.Id>post<Articles>エンドポイントと同様に、putハンドラーはウェブフォームを使用して送信されたフォームパラメーターを受信します。delete<Articles.Id>このルートハンドラーは、指定された識別子を持つ記事を削除します。
完全な例はこちらにあります: resource-routing
リソースからのリンク構築
ルーティングにリソース定義を使用するだけでなく、リンクの構築にも使用できます。 これはリバースルーティング(reverse routing)と呼ばれることもあります。 リソースからのリンク構築は、HTML DSLで作成されたHTMLドキュメントにこれらのリンクを追加する必要がある場合や、リダイレクトレスポンスを生成する必要がある場合に役立ちます。
Resourcesプラグインは、オーバーロードされたhrefメソッドでApplicationを拡張しており、これによりResourceからリンクを生成できます。例えば、以下のコードスニペットは、上記で定義したEditリソースのリンクを作成します。
val link: String = href(Articles.Id.Edit(Articles.Id(id = 123)))親リソースであるArticlesがsortクエリパラメーターをデフォルト値newで定義しているため、link変数には以下が含まれます。
/articles/123/edit?sort=newホストとプロトコルを指定するURLを生成するには、hrefメソッドにURLBuilderを渡すことができます。 この例に示すように、追加のクエリパラメーターもURLBuilderを使用して指定できます。
val urlBuilder = URLBuilder(URLProtocol.HTTPS, "ktor.io", parameters = parametersOf("token", "123"))
href(Articles(sort = null), urlBuilder)
val link: String = urlBuilder.buildString()その後、link変数には以下が含まれます。
https://ktor.io/articles?token=123例
以下の例は、リソースから構築されたリンクをHTMLレスポンスに追加する方法を示しています。
get {
call.respondHtml {
body {
this@module.apply {
p {
val link: String = href(Articles())
a(link) { +"Get all articles" }
}
p {
val link: String = href(Articles.New())
a(link) { +"Create a new article" }
}
p {
val link: String = href(Articles.Id.Edit(Articles.Id(id = 123)))
a(link) { +"Edit an exising article" }
}
p {
val urlBuilder = URLBuilder(URLProtocol.HTTPS, "ktor.io", parameters = parametersOf("token", "123"))
href(Articles(sort = null), urlBuilder)
val link: String = urlBuilder.buildString()
i { a(link) { +link } }
}
}
}
}
}完全な例はこちらにあります: resource-routing
