Skip to content

2.2.x から 3.0.x への移行

このガイドでは、Ktor アプリケーションを 2.2.x バージョンから 3.0.x へ移行する方法について説明します。

Ktor サーバー

ApplicationEngineApplicationEnvironment、および Application

設定可能性を向上させ、ApplicationEngineApplicationEnvironment、および Application インスタンス間の分離をより明確にするために、いくつかの設計変更が導入されました。

v3.0.0 より前は、ApplicationEngineApplicationEnvironment を管理し、さらに ApplicationEnvironmentApplication を管理していました。

しかし、現在の設計では、ApplicationApplicationEngineApplicationEnvironment の両方を作成、所有、および開始する責任を負います。

この再構築に伴い、以下の破壊的変更(breaking changes)が発生します。

これらの変更は、以前のモデルに依存している既存のコードに影響を与えます。

クラス名の変更

パッケージ2.x.x3.0.x
io.ktor:ktor-server-coreApplicationEngineEnvironmentBuilderApplicationEnvironmentBuilder
io.ktor:ktor-server-coreapplicationEngineEnvironmentapplicationEnvironment

start() および stop() メソッドが ApplicationEngineEnvironment から削除されました

ApplicationEngineEnvironmentApplicationEnvironment に統合されたことに伴い、start() および stop() メソッドは ApplicationEngine を通じてのみアクセス可能になりました。

2.x.x3.0.x
ApplicationEngineEnvironment.start()ApplicationEngine.start()
ApplicationEngineEnvironment.stop()ApplicationEngine.stop()

さらに、以下の表は削除されたプロパティと、それに対応する現在の所有関係を示しています。

2.x.x3.0.x
ApplicationEngineEnvironment.connectorsApplicationEngine.Configuration.connectors
ApplicationEnvironment.developmentModeApplication.developmentMode
ApplicationEnvironment.monitorApplication.monitor
ApplicationEnvironment.parentCoroutineContextApplication.parentCoroutineContext
ApplicationEnvironment.rootPathApplication.rootPath

所有関係の変更は、以下の例で確認できます。

commandLineEnvironment() が削除されました

コマンドライン引数から ApplicationEngineEnvironment インスタンスを作成するために使用されていた commandLineEnvironment() 関数は、Ktor 3.0.0 で削除されました。代わりに、CommandLineConfig 関数を使用して、コマンドライン引数を設定オブジェクトにパースできます。

アプリケーションを commandLineEnvironment から CommandLineConfig に移行するには、以下に示すように commandLineEnvironment()configure ブロックに置き換えてください。

embeddedServer を使用したコマンドライン設定の詳細については、コードでの設定のトピックを参照してください。

ServerConfigBuilder の導入

サーバープロパティを設定するための新しいエンティティである ServerConfigBuilder が導入され、以前の ApplicationPropertiesBuilder 設定メカニズムを置き換えました。 ServerConfigBuilderServerConfig クラスのインスタンスを構築するために使用されます。このクラスは、以前 ApplicationProperties によって管理されていたモジュール、パス、および環境の詳細を保持するようになりました。

主な変更点は以下の通りです。

パッケージ2.x.x3.0.x
io.ktor:ktor-server-coreApplicationPropertiesServerConfig
io.ktor:ktor-server-coreApplicationPropertiesBuilderServerConfigBuilder

さらに、embeddedServer() 関数では、この新しい設定アプローチを反映するために applicationProperties 属性が rootConfig に名前変更されました。

embeddedServer() を使用する場合は、applicationProperties 属性を rootConfig に置き換えてください。 以下は、developmentMode を明示的に true に設定してサーバーを構成する serverConfig ブロックの使用例です。

kotlin
fun main(args: Array<String>) {
    embeddedServer(Netty,
        serverConfig {
            developmentMode = true
            module(Application::module)
        },
        configure = {
            connector { port = 12345 }
        }
    ).start(wait = true)
}

EmbeddedServer の導入

EmbeddedServer クラスが導入され、embeddedServer() 関数の戻り値の型として ApplicationEngine の代わりに使用されるようになりました。

モデルの変更に関する詳細は、YouTrack の issue KTOR-3857 を参照してください。

テスト

withTestApplication および withApplication が削除されました

2.0.0 リリースで以前に非推奨となった withTestApplication および withApplication 関数が、ktor-server-test-host パッケージから完全に削除されました。

代わりに、testApplication 関数を既存の Ktor クライアント インスタンスと一緒に使用して、サーバーにリクエストを送信し、結果を検証してください。

以下のテストでは、handleRequest 関数が client.get リクエストに置き換えられています。

詳細については、Ktor Server でのテストを参照してください。

TestApplication のモジュール読み込み

TestApplication は、設定ファイル(例:application.conf)からモジュールを自動的に読み込まなくなりました。代わりに、testApplication 関数内でモジュールを明示的に読み込むか、設定ファイルを手動で読み込む必要があります。

明示的なモジュールの読み込み

モジュールを明示的に読み込むには、testApplication 内で application 関数を使用します。この方法により、読み込むモジュールを手動で指定でき、テストのセットアップをより細かく制御できます。

設定ファイルからモジュールを読み込む

設定ファイルからモジュールを読み込みたい場合は、environment 関数を使用してテスト用の設定ファイルを指定します。

kotlin
@Test
fun testHello() = testApplication {
    environment {
        config = ApplicationConfig("application-custom.conf")
    }
}

テストアプリケーションの設定の詳細については、Ktor Server でのテストセクションを参照してください。

CallLogging プラグインのパッケージ名が変更されました

CallLogging プラグインのパッケージ名が、タイポ修正のため変更されました。

2.x.x3.0.x
io.ktor.server.plugins.calllogingio.ktor.server.plugins.calllogging

ktor-server-host-common モジュールが削除されました

ApplicationApplicationEngine の情報を必要とするようになったため、ktor-server-host-common モジュールの内容は ktor-server-core、具体的には io.ktor.server.engine パッケージに統合されました。

依存関係が適切に更新されていることを確認してください。ほとんどの場合、ktor-server-host-common の依存関係を削除するだけで済みます。

Locations プラグインが削除されました

Ktor サーバー用の Locations プラグインが削除されました。型安全なルーティングを作成するには、代わりに Resources プラグイン を使用してください。これには以下の変更が必要です。

  • アーティファクト io.ktor:ktor-server-locationsio.ktor:ktor-server-resources に置き換えます。

  • Resources プラグインは Kotlin serialization プラグインに依存しています。シリアライゼーションプラグインを追加するには、kotlinx.serialization のセットアップを参照してください。

  • プラグインのインポートを io.ktor.server.locations.* から io.ktor.server.resources.* に更新します。

  • さらに、io.ktor.resources から Resource モジュールをインポートします。

以下の例は、これらの変更を実装する方法を示しています。

Resources の使用方法に関する詳細は、型安全なルーティングを参照してください。

WebSockets 設定における java.time の置き換え

WebSockets プラグインの設定が更新され、pingPeriod および timeout プロパティに Kotlin の Duration を使用するようになりました。これにより、以前使用されていた java.time.Duration が置き換えられ、より Kotlin らしい記述が可能になりました。

既存のコードを新しい形式に移行するには、kotlin.time.Duration クラスの拡張関数やプロパティを使用して期間を構築します。以下の例では、Duration.ofSeconds() が Kotlin の seconds 拡張プロパティに置き換えられています。

必要に応じて、他の期間設定でも同様の Kotlin duration 拡張(minuteshours など)を使用できます。詳細については、Duration のドキュメントを参照してください。

サーバーソケットの .bind() が中断関数になりました

JS および WasmJS 環境での非同期操作をサポートするため、TCPSocketBuilderUDPSocketBuilder の両方におけるサーバーソケットの .bind() 関数が中断関数(suspending function)に更新されました。つまり、.bind() への呼び出しはすべてコルーチン内で行う必要があります。

移行するには、.bind() がコルーチンまたは中断関数内でのみ呼び出されるようにしてください。以下は runBlocking を使用した例です。

kotlin
  runBlocking {
    val selectorManager = SelectorManager(Dispatchers.IO)
    val serverSocket = aSocket(selectorManager).tcp().bind("127.0.0.1", 9002)
    //...
}

ソケットの操作に関する詳細は、ソケットのドキュメントを参照してください。

マルチパートフォームデータ

バイナリおよびファイル項目の新しいデフォルト制限

Ktor 3.0.0 では、ApplicationCall.receiveMultipart() を使用してバイナリおよびファイル項目を受信する際のデフォルト制限として 50MiB が導入されました。受信したファイルまたはバイナリ項目が 50MiB の制限を超えると、IOException がスローされます。

デフォルト制限のオーバーライド

明示的な設定なしに 50MiB を超えるファイルを処理していたアプリケーションの場合は、予期しない動作を避けるためにコードを更新する必要があります。

デフォルトの制限をオーバーライドするには、.receiveMultipart() を呼び出す際に formFieldLimit パラメータを渡します。

kotlin
val multipartData = call.receiveMultipart(formFieldLimit = 1024 * 1024 * 100)

PartData.FileItem.streamProvider() は非推奨となりました

Ktor の以前のバージョンでは、PartData.FileItem.streamProvider() 関数を使用して、ファイル項目の内容を InputStream として取得していました。Ktor 3.0.0 以降、この関数は非推奨となりました。

アプリケーションを移行するには、.streamProvider().provider() 関数に置き換えてください。.provider() 関数は ByteReadChannel を返します。これは、バイトストリームをインクリメンタルに読み取るための、コルーチンフレンドリーで非ブロッキングな抽象化です。 その後、ByteReadChannel が提供する .copyTo() または .copyAndClose() メソッドを使用して、チャネルからファイル出力へ直接データをストリーミングできます。

以下の例では、.copyAndClose() メソッドを使用して ByteReadChannel からファイルの WritableByteChannel へデータを転送しています。

マルチパートフォームデータの操作に関する完全な例と詳細は、マルチパートフォームデータのリクエスト処理を参照してください。

セッション暗号化方式の更新

セキュリティを強化するため、Sessions プラグインが提供する暗号化方式が更新されました。

具体的には、以前は復号されたセッション値から MAC を導出していた SessionTransportTransformerEncrypt メソッドが、暗号化された値から計算するようになりました。

既存のセッションとの互換性を確保するため、Ktor は backwardCompatibleRead プロパティを導入しました。現在の構成を維持するには、SessionTransportTransformerEncrypt のコンストラクタにこのプロパティを含めてください。

kotlin
install(Sessions) {
  cookie<UserSession>("user_session") {
    // ...
    transform(
      SessionTransportTransformerEncrypt(
        secretEncryptKey, // ここに暗号化キー
        secretSignKey, // ここに署名キー
        backwardCompatibleRead = true
      )
    )
  }
}

Ktor でのセッション暗号化の詳細については、セッションデータの署名と暗号化を参照してください。

Ktor クライアント

HttpResponsecontent プロパティの名前変更

Ktor 3.0.0 より前は、HttpResponsecontent プロパティが、ネットワークから読み取られるレスポンスコンテンツへの生の ByteReadChannel を提供していました。Ktor 3.0.0 以降、その目的をより正確に反映するために、content プロパティは rawContent に名前変更されました。

SocketTimeoutException が型エイリアスになりました

io.ktor.client.network.sockets パッケージの SocketTimeoutException が、Kotlin クラスから Java クラスのエイリアスに変更されました。この変更により、特定の場合に NoClassDefFoundError が発生する可能性があり、既存のコードの更新が必要になる場合があります。

アプリケーションを移行するには、コードが古いクラスを参照しておらず、最新の Ktor バージョンでコンパイルされていることを確認してください。例外チェックの更新方法は以下の通りです。

共有モジュール

kotlinx-io への移行

3.0.0 リリースに伴い、Ktor は kotlinx-io ライブラリを使用するように移行しました。これにより、Kotlin ライブラリ間で標準化された効率的な I/O API が提供されます。この変更により、パフォーマンスが向上し、メモリ割り当てが削減され、I/O 処理が簡素化されます。プロジェクトが Ktor の低レベル I/O API とやり取りしている場合は、互換性を確保するためにコードを更新する必要があるかもしれません。

これは、ByteReadChannelByteWriteChannel など、多くのクラスに影響します。さらに、以下の Ktor クラスは kotlinx-io をベースにするようになり、以前の実装は非推奨となりました。

Ktor 2.xKtor 3.x
io.ktor.utils.io.core.Bufferkotlinx.io.Buffer
io.ktor.utils.io.core.BytePacketBuilderkotlinx.io.Sink
io.ktor.utils.io.core.ByteReadPacketkotlinx.io.Source
io.ktor.utils.io.core.Inputkotlinx.io.Source
io.ktor.utils.io.core.Outputkotlinx.io.Sink
io.ktor.utils.io.core.Sinkkotlinx.io.Buffer
io.ktor.utils.io.errors.EOFExceptionkotlinx.io.EOFException
io.ktor.utils.io.errors.IOExceptionkotlinx.io.IOException

非推奨の API は Ktor 4.0 までサポートされますが、できるだけ早く移行することをお勧めします。アプリケーションを移行するには、kotlinx-io の対応するメソッドを使用するようにコードを更新してください。

例:ストリーミング I/O

大きなファイルのダウンロードを処理し、効率的なストリーミングソリューションが必要な場合は、手動のバイト配列処理を kotlinx-io の最適化されたストリーミング API に置き換えることができます。

Ktor 2.x では、大きなファイルのダウンロード処理には通常、ByteReadChannel.readRemaining() を使用して利用可能なバイトを手動で読み取り、File.appendBytes() を使用してファイルに書き込むことが含まれていました。

Kotlin
val client = HttpClient(CIO)
val file = File.createTempFile("files", "index")

runBlocking {
    client.prepareGet("https://ktor.io/").execute { httpResponse ->
        val channel: ByteReadChannel = httpResponse.body()
        while (!channel.isClosedForRead) {
            val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
            while (!packet.isEmpty) {
                val bytes = packet.readBytes()
                file.appendBytes(bytes)
                println("Received ${file.length()} bytes from ${httpResponse.contentLength()}")
            }
        }
        println("A file saved to ${file.path}")
    }
}

このアプローチでは、複数のメモリ割り当てと冗長なデータコピーが発生していました。

Ktor 3.x では、ByteReadChannel.readRemaining()Source を返すようになり、Source.transferTo() を使用したデータのストリーミングが可能になりました。

Kotlin
    val client = HttpClient(CIO)
    val file = File.createTempFile("files", "index")
    val stream = file.outputStream().asSink()
    val fileSize = 100 * 1024 * 1024
    val bufferSize: Long = 1024 * 1024

    runBlocking {
        client.prepareGet("https://httpbin.org/bytes/$fileSize").execute { httpResponse ->
            val channel: ByteReadChannel = httpResponse.body()
            var count = 0L
            stream.use {
                while (!channel.exhausted()) {
                    val chunk = channel.readRemaining(bufferSize)
                    count += chunk.remaining

                    chunk.transferTo(stream)
                    println("Received $count bytes from ${httpResponse.contentLength()}")
                }
            }
        }

        println("A file saved to ${file.path}")
    }

このアプローチでは、チャネルからファイルのシンクへ直接データを転送するため、メモリ割り当てを最小限に抑え、パフォーマンスが向上します。

完全な例については、client-download-streaming を参照してください。

API の置き換えに関する詳細は、kotlinx-io ドキュメントを参照してください。

属性キーに厳密な型一致が必要になりました

Ktor 3.0.0 では、AttributeKey インスタンスは同一性(identity)によって比較され、値を保存および取得する際に厳密な型一致が必要になりました。これにより、型安全性が確保され、型の不一致による意図しない動作が防止されます。

以前は、AttributeKey<Boolean> を取得するために getOrNull<Any>() を使用するなど、保存された時とは異なるジェネリック型で属性を取得することが可能でした。

アプリケーションを移行するには、取得時の型が保存された型と正確に一致することを確認してください。

kotlin
val attrs = Attributes()

attrs.put(AttributeKey<Boolean>("key"), true)
attrs.getOrNull<Boolean>("key")

空のアーティファクトの削除

Ktor 1.0.0 以降、空のアーティファクト io.ktor:ktor が誤って Maven に公開されていました。このアーティファクトは Ktor 3.0.0 から削除されました。

プロジェクトにこのアーティファクトが依存関係として含まれている場合は、安全に削除できます。