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を管理し、それがさらにApplicationを管理していました。

しかし、現在の設計では、ApplicationApplicationEngineApplicationEnvironmentの両方を作成、所有、初期化する責任を負います。

この再構築には、以下の破壊的変更が伴います。

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

クラス名の変更

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

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

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の設定メカニズムを置き換えます。ServerConfigBuilderは、以前ApplicationPropertiesによって管理されていたモジュール、パス、および環境の詳細を保持するServerConfigクラスのインスタンスを構築するために使用されます。

以下の表は、主な変更点をまとめたものです。

パッケージ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のKTOR-3857の課題を参照してください。

テスト

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

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

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

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

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

TestApplicationモジュールの読み込み

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

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

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

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

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

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

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

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-locationsアーティファクトをio.ktor:ktor-server-resourcesに置き換えます。

  • ResourcesプラグインはKotlin serializationプラグインに依存します。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期間拡張(minuteshoursなど)を使用できます。詳細については、Durationのドキュメントを参照してください。

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

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

移行するには、.bind()がコルーチンまたはsuspend関数内でのみ呼び出されるようにしてください。以下は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()を使用してバイナリおよびファイルアイテムを受信するためのデフォルトの制限が50MBに導入されました。受信したファイルまたはバイナリアイテムが50MBの制限を超えると、IOExceptionがスローされます。

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

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

デフォルト制限をオーバーライドするには、.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メソッドが、暗号化された値からMACを計算するようになりました。

既存のセッションとの互換性を確保するため、KtorはbackwardCompatibleReadプロパティを導入しました。現在の設定では、SessionTransportTransformerEncryptのコンストラクタにこのプロパティを含めます。

kotlin
install(Sessions) {
  cookie<UserSession>("user_session") {
    // ...
    transform(
      SessionTransportTransformerEncrypt(
        secretEncryptKey, // your encrypt key here
        secretSignKey, // your sign key here
        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

    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()
                    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ドキュメントを参照してください。

Attributeキーが厳密な型の一致を要求するようになりました

Ktor 3.0.0では、AttributeKeyインスタンスはIDによって比較され、値を格納および取得する際に厳密な型の一致を要求するようになりました。これにより、型安全性が確保され、型不一致によって引き起こされる意図しない動作が防止されます。

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

アプリケーションを移行するには、取得型が格納型と厳密に一致することを確認してください。

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以降削除されました。

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