接收回應
所有用於發出 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
標頭值中的字元集。etag
用於E-Tag
標頭值。setCookie
用於Set-Cookie
標頭值。
Ktor 還提供了 HttpCookies 外掛程式,允許您在呼叫之間保留 Cookie。
接收回應主體
原始主體
若要接收回應的原始主體,請呼叫 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()
若要了解更多資訊,請參閱接收和傳送資料。
串流資料
當您呼叫 HttpResponse.body
函數來獲取主體時,Ktor 會在記憶體中處理回應並傳回完整的回應主體。如果您需要依序取得回應的區塊(chunk),而不是等待整個回應,請使用具有範圍 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}")
}