レスポンスの受信
HTTPリクエストの作成に使用されるすべての関数 (request
、get
、post
など) を使用すると、 HttpResponse
オブジェクトとしてレスポンスを受信できます。
HttpResponse
は、レスポンスボディをさまざまな方法 (生のバイト、JSON オブジェクトなど) で取得し、 ステータスコード、コンテンツタイプ、ヘッダーなどのレスポンスパラメーターを取得するために必要なAPIを公開します。 たとえば、パラメーターなしの GET
リクエストに対する HttpResponse
は次のように受信できます。
val response: HttpResponse = client.get("https://ktor.io/docs/welcome.html")
レスポンスパラメーターの受信
HttpResponse
クラスを使用すると、ステータスコード、ヘッダー、HTTPバージョンなど、さまざまなレスポンスパラメーターを取得できます。
ステータスコード
レスポンスのステータスコードを取得するには、 HttpResponse.status
プロパティを使用します。
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.http.*
val httpResponse: HttpResponse = client.get("https://ktor.io/")
if (httpResponse.status.value in 200..299) {
println("Successful response!")
}
ヘッダー
HttpResponse.headers
プロパティを使用すると、すべてのレスポンスヘッダーを含む Headers マップを取得できます。 さらに、HttpResponse
は特定のヘッダー値を受信するための以下の関数を公開しています。
contentType
はContent-Type
ヘッダー値用です。charset
はContent-Type
ヘッダー値からのcharset用です。etag
はE-Tag
ヘッダー値用です。setCookie
はSet-Cookie
ヘッダー値用です。Ktor は、呼び出し間で Cookie を保持できる HttpCookies プラグインも提供しています。
レスポンスボディの受信
生のボディ
レスポンスの生のボディを受信するには、body
関数を呼び出し、必要な型をパラメーターとして渡します。 以下のコードスニペットは、生のボディをString
として受信する方法を示しています。
val httpResponse: HttpResponse = client.get("https://ktor.io/")
val stringBody: String = httpResponse.body()
同様に、ByteArray
としてボディを取得できます。
val httpResponse: HttpResponse = client.get("https://ktor.io/")
val byteArrayBody: ByteArray = httpResponse.body()
以下の実行可能な例は、 レスポンスをByteArray
として取得し、ファイルに保存する方法を示しています。
val client = HttpClient()
val file = File.createTempFile("files", "index")
runBlocking {
val httpResponse: HttpResponse = client.get("https://ktor.io/") {
onDownload { bytesSentTotal, contentLength ->
println("Received $bytesSentTotal bytes from $contentLength")
}
}
val responseBody: ByteArray = httpResponse.body()
file.writeBytes(responseBody)
println("A file saved to ${file.path}")
}
上記の例のonDownload()
拡張関数は、ダウンロードの進行状況を表示するために使用されます。
ストリーミングではないリクエストの場合、レスポンスボディは自動的にメモリにロードされキャッシュされるため、繰り返しアクセスできます。 これは小さなペイロードに対しては効率的ですが、大きなレスポンスの場合にはメモリ使用量が高くなる可能性があります。
大規模なレスポンスを効率的に処理するには、レスポンスをメモリに保存せずに増分的に処理するストリーミングアプローチを使用します。
JSON オブジェクト
ContentNegotiation プラグインがインストールされている場合、レスポンスを受信する際にJSONデータをデータクラスにデシリアライズできます。
val customer: Customer = client.get("http://localhost:8080/customer/3").body()
詳細については、データの送受信を参照してください。
ContentNegotiation プラグインは、クライアントとサーバーの両方で利用できます。ご自身のケースに適切なものを使用してください。
ストリーミングデータ
HttpResponse.body
関数を呼び出してボディを取得すると、Ktor はレスポンスをメモリで処理し、完全なレスポンスボディを返します。 レスポンス全体を待つのではなく、レスポンスのチャンクを順番に取得する必要がある場合は、スコープ付きの execute ブロックで HttpStatement
を使用します。 以下の実行可能な例は、 レスポンスコンテンツをチャンク (バイトパケット) で受信し、ファイルに保存する方法を示しています。
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}")
}
この例では、ByteReadChannel
を使用してデータを非同期に読み取ります。ByteReadChannel.readRemaining()
を使用すると、チャネル内の利用可能なすべてのバイトが取得され、 Source.transferTo()
はデータをファイルに直接書き込むため、不要な割り当てが削減されます。
追加の処理なしでレスポンスボディをファイルに保存するには、代わりに ByteReadChannel.copyAndClose()
関数を使用できます。
client.prepareGet("https://httpbin.org/bytes/$fileSize").execute { httpResponse ->
val channel: ByteReadChannel = httpResponse.body()
channel.copyAndClose(file.writeChannel())
println("A file saved to ${file.path}")
}