型安全なルーティング

Estimated reading time: 2 minutes

Ktor は URL の構築とリクエストパラメータの両方に対し、型安全にルーティングする機構を提供しています。

Locations は試験的な機能 (experimental feature) です。

目次:

This feature is defined in the class io.ktor.locations.Locations in the artifact io.ktor:ktor-locations:$ktor_version.
dependencies { implementation "io.ktor:ktor-locations:$ktor_version" }
dependencies { implementation("io.ktor:ktor-locations:$ktor_version") }
<project> ... <dependencies> <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-locations</artifactId> <version>${ktor.version}</version> <scope>compile</scope> </dependency> </dependencies> </project>

Location feature のインストール

Location feature の利用には特別な設定は不要です。 単に Location feature をインストールするだけで利用可能です。

install(Locations)

Locations は試験的な機能です。 @KtorExperimentalLocationsAPI アノテーションを付与することで Warning を抑制できます。

Route クラスの定義

型付きルーティングごとに、ルーティング内のパラメータを持つクラス (通常は data class) を作成します。

パラメータの型は Data Conversion feature でサポートされている型でなければなりません。 デフォルトでは、 IntLongFloatDoubleBooleanStringEnumIterable を指定できます。

URL パラメータ

ルーティング用のクラスに @Location アノテーションを付与し、 {} で囲まれたプレースホルダー ({propertyName} など) と同名のプロパティを定義します。

@Location("/list/{name}/page/{page}")
data class Listing(val name: String, val page: Int)
  • /list/movies/page/10 にマッチする
  • Listing(name = "movies", page = 10) が生成される

GET パラメータ

@Location アノテーション内のプレースホルダーに無いプロパティを定義した場合、 GET クエリまたは POST パラメータで指定された値が格納されます。

@Location("/list/{name}")
data class Listing(val name: String, val page: Int, val count: Int)
  • /list/movies?page=10&count=20 にマッチする
  • Listing(name = "movies", page = 10, count = 20) が生成される

ルーティングハンドラの定義

@Location アノテーションを付与した Route クラス を定義すると、 Location feature はその型の getoptionsheaderpostputdeletepatch ルーティングハンドラを 生成します。

routing {
    get<Listing> { listing ->
        call.respondText("Listing ${listing.name}, page ${listing.page}")
    }
}

io.ktor.locations にて型パラメータを1つ持つジェネリックなメソッドが複数定義されています。 io.ktor.routing にも定義されている関数とほぼ同一のインタフェースで実装されています。 locations パッケージの前に routing パッケージをインポートすると、これらのパッケージをインポートする代わりに、 これらのメソッドを一般化するよう IDE が提案することがあります。 (import io.ktor.locations.* を手動で追加すると発生します。) Locations API はまだ試験的な機能です。 この問題はすでに GitHub で報告 されています。

URL の構築

@Location アノテーションが付与されたクラスのインスタンスを application.locations.href の引数に渡すことで、 そのルーティングへのURLが構築されます。

val path = application.locations.href(Listing(name = "movies", page = 10, count = 20))

この例では、変数 path は文字列 "/list/movies?page=10&count=20" になります。

@Location("/list/{name}") data class Listing(val name: String, val page: Int, val count: Int)

この方式で URL を生成するようにすることで、もし URL を変更しようとした際は @Location パスを更新するだけで済むので非常に便利です。

パラメータを伴うネストしたルーティング

パラメータを伴うルーティングをネストさせる場合、 @Location が付与された上位のクラス (Type) をプロパティ (val type: Type) に持つ @Location が付与された内部クラス (EditList) を作成し、下記のようにルーティングに登録します。

routing {
    get<Type.Edit> { typeEdit -> // /type/{name}/edit
        // ...
    }
    get<Type.List> { typeList -> // /type/{name}/list/{page}
        // ...
    }
}
@Location("/type/{name}") data class Type(val name: String) {
    // In these classes we have to include the `name` property matching the parent.
    @Location("/list/{page}") data class List(val type: Type, val page: Int)		@Location("/edit") data class Edit(val parent: Type)
    @Location("/list/{page}") data class List(val parent: Type, val page: Int)
}