型安全なルーティング
必須の依存関係: io.ktor:ktor-server-resources
コード例: resource-routing
Ktorは、型安全なルーティングを実装できるResourcesプラグインを提供します。これを実現するには、型付きルートとして機能するクラスを作成し、このクラスに@Resourceキーワードを使用してアノテーションを付ける必要があります。なお、@Resourceアノテーションはkotlinx.serializationライブラリによって提供される@Serializableの動作を持ちます。
Ktorクライアントは、サーバーに対して型付けされたリクエストを行う機能を提供します。
依存関係の追加
kotlinx.serializationの追加
リソースクラスが@Serializableの動作を持つべきであるため、セットアップセクションで説明されているように、Kotlinシリアライゼーションプラグインを追加する必要があります。
Resourcesの依存関係の追加
Resourcesを使用するには、ビルドスクリプトにktor-server-resourcesアーティファクトを含める必要があります:
Resourcesのインストール
アプリケーションにResourcesプラグインをインストールするには、 指定された
install関数に渡します。 以下のコードスニペットは、Resourcesをインストールする方法を示しています... - ...
embeddedServer関数呼び出し内。 - ... 明示的に定義された
module内(Applicationクラスの拡張関数)。
リソースクラスの作成
各リソースクラスには@Resourceアノテーションが必要です。 以下では、単一のパスセグメント、クエリパラメーターやパスパラメーターなどを定義する、いくつかのリソースクラスの例を見ていきます。
リソースURL
以下の例は、/articlesパスに応答するリソースを指定するArticlesクラスを定義する方法を示しています。
import io.ktor.resources.*
@Resource("/articles")
class Articles()クエリパラメーターを持つリソース
以下のArticlesクラスは、クエリパラメーターとして機能するsort文字列プロパティを持ち、sortクエリパラメーターを含む次のパス(/articles?sort=new)に応答するリソースを定義できます。
@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 ->
// Get all articles ...
call.respondText("List of articles: $articles")
}
}
}以下の例は、例: CRUD操作のためのリソースで作成されたArticlesリソースのルートハンドラーを定義する方法を示しています。ルートハンドラー内では、Articleにパラメーターとしてアクセスし、そのプロパティ値を取得できることに注意してください。
fun Application.module() {
install(Resources)
routing {
get<Articles> { article ->
// Get all articles ...
call.respondText("List of articles sorted starting from ${article.sort}")
}
get<Articles.New> {
// Show a page with fields for creating a new article ...
call.respondText("Create a new article")
}
post<Articles> {
// Save an article ...
call.respondText("An article is saved", status = HttpStatusCode.Created)
}
get<Articles.Id> { article ->
// Show an article with id ${article.id} ...
call.respondText("An article with id ${article.id}", status = HttpStatusCode.OK)
}
get<Articles.Id.Edit> { article ->
// Show a page with fields for editing an article ...
call.respondText("Edit an article with id ${article.parent.id}", status = HttpStatusCode.OK)
}
put<Articles.Id> { article ->
// Update an article ...
call.respondText("An article with id ${article.id} updated", status = HttpStatusCode.OK)
}
delete<Articles.Id> { article ->
// Delete an 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。
リソースからリンクを構築する
リソース定義をルーティングに使用するだけでなく、リンクを構築するためにも使用できます。 これは_リバースルーティング_と呼ばれることもあります。 リソースからリンクを構築することは、HTML DSLで作成されたHTMLドキュメントにこれらのリンクを追加する必要がある場合や、リダイレクト応答を生成する必要がある場合に役立ちます。
Resourcesプラグインは、Applicationをオーバーロードされたhrefメソッドで拡張し、Resourceからリンクを生成できるようにします。たとえば、以下のコードスニペットは、上記で定義されたEditリソースのリンクを作成します。
val link: String = href(Articles.Id.Edit(Articles.Id(id = 123)))祖先のArticlesリソースがデフォルト値newを持つsortクエリパラメーターを定義しているため、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。
