型安全なルーティング
必須の依存関係: 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。