Skip to content

Ktor 3.2.0の新機能

リリース日: 2025年6月12日

この機能リリースの主なハイライトは以下のとおりです。

Ktorサーバー

サスペンド可能なモジュール関数

Ktor 3.2.0から、アプリケーションモジュールがサスペンド関数をサポートするようになりました。

以前は、Ktorモジュール内で非同期関数を追加するには、サーバー作成時にデッドロックを引き起こす可能性のあるrunBlockingブロックが必要でした。

kotlin
fun Application.installEvents() {
    val kubernetesConnection = runBlocking {
        connect(property<KubernetesConfig>("app.events"))
    }
}

これでsuspendキーワードを使用できるようになり、アプリケーション起動時に非同期コードが記述できます。

kotlin
suspend fun Application.installEvents() {
    val kubernetesConnection = connect(property<KubernetesConfig>("app.events"))
}

並行モジュール読み込み

ktor.application.startup = concurrentというGradleプロパティを追加することで、並行モジュール読み込みをオプトインすることもできます。 これにより、すべてのアプリケーションモジュールが独立して起動されるため、1つがサスペンドしても他のモジュールはブロックされません。 これにより、依存性注入のための非シーケンシャルな読み込みが可能になり、場合によっては読み込み速度が向上します。

詳細については、並行モジュール読み込みを参照してください。

設定ファイルのデシリアライゼーション

Ktor 3.2.0では、Applicationクラスの新しい.property()拡張関数により、型付き設定ロードが導入されました。これにより、構造化された設定セクションをKotlinデータクラスに直接デシリアライズできるようになります。

この機能により、設定値へのアクセスが簡素化され、ネストされた設定やグループ化された設定を扱う際のボイラープレートが大幅に削減されます。

以下のapplication.yamlファイルを考えてみましょう。

yaml
database:
   url: "$DATABASE_URL:jdbc:postgresql://localhost:5432/postgres"
   username: "$DATABASE_USER:ktor_admin"
   password: "$DATABASE_PASSWORD:ktor123!"

以前は、各設定値を個別に取得する必要がありました。新しい.property()拡張関数を使用すると、設定セクション全体を一度にロードできます。

この機能は、HOCONとYAMLの両方の設定形式をサポートし、デシリアライゼーションにはkotlinx.serializationを使用します。

ApplicationTestBuilderclientを設定可能に

Ktor 3.2.0から、ApplicationTestBuilderクラスのclientプロパティが可変になりました。以前は読み取り専用でした。 この変更により、独自のテストクライアントを設定し、ApplicationTestBuilderクラスが利用可能な場所であればどこでも再利用できるようになります。例えば、拡張関数内からクライアントにアクセスできます。

kotlin
@Test
fun testRouteAfterAuthorization() = testApplication {
    // Pre-configure the client
    client = createClient {
        install(ContentNegotiation) {
            json()
        }
            
        defaultRequest { 
            contentType(ContentType.Application.Json)
        }
    }

    // Reusable test step extracted into an extension-function
    auth(token = AuthToken("swordfish"))

    val response = client.get("/route")
    assertEquals(OK, response.status)
}

private fun ApplicationTestBuilder.auth(token: AuthToken) {
    val response = client.post("/auth") {
        setBody(token)
    }
    assertEquals(OK, response.status)
}

依存性注入

Ktor 3.2.0では、依存性注入 (DI) のサポートが導入され、設定ファイルやアプリケーションコードから直接依存関係を管理・接続することが容易になりました。新しいDIプラグインは、依存関係の解決を簡素化し、非同期ロードをサポートし、自動クリーンアップを提供し、テストとのスムーズな統合を実現します。

DIを使用するには、ktor-server-diアーティファクトをビルドスクリプトに含めます。

Kotlin
Groovy
XML

基本的な依存関係の登録

ラムダ、関数参照、またはコンストラクタ参照を使用して依存関係を登録できます。

kotlin
dependencies {
  // Lambda-based
  provide<GreetingService> { GreetingServiceImpl() }

  // Function references
  provide<GreetingService>(::GreetingServiceImpl)
  provide(BankServiceImpl::class)
  provide(::createBankTeller)

  // Registering a lambda as a dependency
  provide<() -> GreetingService> { { GreetingServiceImpl() } }
}

設定ベースの依存関係登録

設定ファイルでクラスパス参照を使用して、宣言的に依存関係を設定できます。これは関数とクラスの両方の参照をサポートします。

yaml
# application.yaml
ktor:
  application:
    dependencies:
      - com.example.RepositoriesKt.provideDatabase
      - com.example.UserRepository
database:
  connectionUrl: postgres://localhost:3037/admin
kotlin
// Repositories.kt
fun provideDatabase(@Property("database.connectionUrl") connectionUrl: String): Database =
  PostgresDatabase(connectionUrl)

class UserRepository(val db: Database) {
  // implementation 
}

引数は、@Property@Namedのようなアノテーションを介して自動的に解決されます。

依存関係の解決と注入

依存関係の解決

依存関係を解決するには、プロパティ委譲または直接解決を使用できます。

kotlin
// Using property delegation
val service: GreetingService by dependencies

// Direct resolution
val service = dependencies.resolve<GreetingService>()
非同期依存関係解決

非同期ロードをサポートするために、サスペンド関数を使用できます。

kotlin
suspend fun Application.installEvents() {
  val kubernetesConnection = dependencies.resolve() // suspends until provided
}

suspend fun Application.loadEventsConnection() {
  dependencies.provide {
    connect(property<KubernetesConfig>("app.events"))
  }
}

DIプラグインは、すべての依存関係が準備されるまでresolve()呼び出しを自動的にサスペンドします。

アプリケーションモジュールへの注入

モジュールパラメータを指定することで、依存関係をアプリケーションモジュールに直接注入できます。KtorはDIコンテナからそれらを解決します。

yaml
ktor:
  application:
    dependencies:
      - com.example.PrintStreamProviderKt
    modules:
      - com.example.LoggingKt.logging
kotlin
fun Application.logging(printStreamProvider: () -> PrintStream) {
    dependencies {
        provide<Logger> { SimpleLogger(printStreamProvider()) }
    }
}

特定のキーを持つ依存関係を注入するには、@Namedを使用します。

kotlin
fun Application.userRepository(@Named("mongo") database: Database) {
    // Uses the dependency named "mongo"
}
プロパティと設定の注入

@Propertyを使用して、設定値を直接注入します。

yaml
connection:
  domain: api.example.com
  path: /v1
  protocol: https
kotlin
val connection: Connection = application.property("connection")

これにより、構造化された設定の作業が簡素化され、プリミティブ型の自動パースがサポートされます。

詳細と高度な使用法については、依存性注入を参照してください。

Ktorクライアント

SaveBodyPluginHttpRequestBuilder.skipSavingBody()は非推奨に

Ktor 3.2.0以前は、SaveBodyPluginがデフォルトでインストールされていました。これはレスポンスボディ全体をメモリにキャッシュし、複数回アクセスできるようにしていました。レスポンスボディの保存を避けるには、このプラグインを明示的に無効にする必要がありました。

Ktor 3.2.0から、SaveBodyPluginは非推奨となり、すべての非ストリーミングリクエストに対してレスポンスボディを自動的に保存する新しい内部プラグインに置き換えられました。これにより、リソース管理が改善され、HTTPレスポンスのライフサイクルが簡素化されます。

HttpRequestBuilder.skipSavingBody()も非推奨です。ボディをキャッシュせずにレスポンスを処理する必要がある場合は、代わりにストリーミングアプローチを使用してください。

このアプローチはレスポンスを直接ストリーミングし、ボディがメモリに保存されるのを防ぎます。

.wrapWithContent().wrap()拡張関数は非推奨に

Ktor 3.2.0では、.wrapWithContent()および.wrap()拡張関数は、新しい.replaceResponse()関数に置き換えられ非推奨になりました。

.wrapWithContent().wrap()関数は、元のレスポンスボディをByteReadChannelに置き換えますが、これは一度しか読み取ることができません。 同じチャネルインスタンスが関数(新しいチャネルを返す)の代わりに直接渡されると、ボディを複数回読み取ることができません。 これにより、レスポンスボディにアクセスする異なるプラグイン間の互換性が失われる可能性があります。なぜなら、最初にボディを読み取るプラグインがボディを消費してしまうからです。

kotlin
// Replaces the body with a channel decoded once from rawContent
val decodedBody = decode(response.rawContent)
val decodedResponse = call.wrapWithContent(decodedBody).response

// The first call returns the body
decodedResponse.bodyAsText()

// Subsequent calls return an empty string
decodedResponse.bodyAsText()

この問題を回避するには、代わりに.replaceResponse()関数を使用してください。 これは、アクセスごとに新しいチャネルを返すラムダを受け入れ、他のプラグインとの安全な統合を保証します。

kotlin
// Replaces the body with a new decoded channel on each access
call.replaceResponse {
    decode(response.rawContent)
}

解決されたIPアドレスへのアクセス

io.ktor.network.sockets.InetSocketAddressインスタンスの新しい.resolveAddress()関数を使用できるようになりました。 この関数を使用すると、関連付けられたホストの解決された生IPアドレスを取得できます。

kotlin
val address = InetSocketAddress("sample-proxy-server", 1080)
val rawAddress = address.resolveAddress()

これは、解決されたIPアドレスをByteArrayとして返します。アドレスを解決できない場合はnullを返します。 返されるByteArrayのサイズはIPバージョンによって異なり、IPv4アドレスの場合は4バイト、IPv6アドレスの場合は16バイトになります。 JSおよびWasmプラットフォームでは、.resolveAddress()は常にnullを返します。

共通

HTMX統合

Ktor 3.2.0では、hx-gethx-swapなどのHTML属性を介した動的なインタラクションを可能にするモダンなJavaScriptライブラリであるHTMXの実験的サポートが導入されました。KtorのHTMX統合は以下を提供します。

  • ヘッダーに基づいてHTMXリクエストを処理するHTMX対応ルーティング。
  • KotlinでHTMX属性を生成するためのHTML DSL拡張。
  • 文字列リテラルを排除するためのHTMXヘッダー定数と値。

KtorのHTMXサポートは、3つの実験的モジュールで利用可能です。

モジュール説明
ktor-htmxコア定義とヘッダー定数
ktor-htmx-htmlKotlin HTML DSLとの統合
ktor-server-htmxHTMX固有のリクエストのルーティングサポート

すべてのAPIは@ExperimentalKtorApiでマークされており、@OptIn(ExperimentalKtorApi::class)によるオプトインが必要です。 詳細については、HTMX統合を参照してください。

Unixドメインソケット

3.2.0では、KtorクライアントをUnixドメインソケットに接続し、Ktorサーバーをそのようなソケットをリッスンするように設定できます。 現在、UnixドメインソケットはCIOエンジンでのみサポートされています。

サーバー設定の例:

kotlin
val server = embeddedServer(CIO, configure = {
    unixConnector("/tmp/test-unix-socket-ktor.sock")
}) {
    routing {
        get("/") {
            call.respondText("Hello, Unix socket world!")
        }
    }
}

Ktorクライアントを使用してそのソケットに接続する例:

kotlin
val client = HttpClient(CIO)

val response: HttpResponse = client.get("/") {
    unixSocket("/tmp/test-unix-socket-ktor.sock")
}

デフォルトリクエストでもUnixドメインソケットを使用できます。

インフラストラクチャ

公開されたバージョンカタログ

このリリースにより、公式の公開されたバージョンカタログを使用して、すべてのKtor依存関係を一元的に管理できるようになりました。これにより、依存関係でKtorのバージョンを手動で宣言する必要がなくなります。

カタログをプロジェクトに追加するには、settings.gradle.ktsでGradleのバージョンカタログを設定し、モジュールのbuild.gradle.ktsファイルで参照します。

kotlin
dependencyResolutionManagement {
    versionCatalogs {
        create("ktorLibs") {
            from("io.ktor:ktor-version-catalog:3.2.3")
        }
    }
}
kotlin
dependencies {
    implementation(ktorLibs.client.core)
    implementation(ktorLibs.client.cio)
    // ...
}

Gradleプラグイン

開発モードの有効化

Ktor 3.2.0では、開発モードの有効化が簡素化されました。以前は、開発モードを有効にするにはapplicationブロックで明示的な設定が必要でした。現在では、ktor.developmentプロパティを使用して、動的または明示的に有効にできます。

  • プロジェクトプロパティに基づいて開発モードを動的に有効にする。

    kotlin
    ktor {
        development = project.ext.has("development")
    }
  • 開発モードを明示的にtrueに設定する。

    kotlin
    ktor {
        development = true
    }

デフォルトでは、ktor.developmentの値は、Gradleプロジェクトプロパティまたはシステムプロパティio.ktor.developmentのいずれかが定義されている場合に、自動的に解決されます。これにより、Gradle CLIフラグを使用して開発モードを直接有効にすることができます。

bash
./gradlew run -Pio.ktor.development=true