Skip to content

接收响应

所有用于发送 HTTP 请求的函数(如 requestgetpost 等)都允许你以 HttpResponse 对象的形式接收响应。

HttpResponse 暴露了所需的 API,用于以各种方式(原始字节、JSON 对象等)获取响应体,以及获取响应参数,例如状态码、内容类型和标头。例如,你可以通过以下方式接收不带参数的 GET 请求的 HttpResponse

kotlin
val response: HttpResponse = client.get("https://ktor.io/docs/welcome.html")

接收响应参数

HttpResponse 类允许你获取各种响应参数,例如状态码、标头、HTTP 版本等。

状态码

要获取响应的状态码,请使用 HttpResponse.status 属性:

kotlin
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 的形式接收原始体:

kotlin
val httpResponse: HttpResponse = client.get("https://ktor.io/")
val stringBody: String = httpResponse.body()

类似地,你可以以 ByteArray 的形式获取体:

kotlin
val httpResponse: HttpResponse = client.get("https://ktor.io/")
val byteArrayBody: ByteArray = httpResponse.body()

下面的可运行示例展示了如何以 ByteArray 的形式获取响应并将其保存到文件:

kotlin
    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 数据反序列化为数据类:

kotlin
val customer: Customer = client.get("http://localhost:8080/customer/3").body()

要了解更多信息,请参见接收和发送数据

ContentNegotiation 插件适用于客户端服务器。请确保针对你的情况使用正确的插件。

流式数据

当你调用 HttpResponse.body 函数以获取体时,Ktor 会在内存中处理响应并返回完整的响应体。如果你需要顺序获取响应块而不是等待整个响应,请使用带作用域 execute 代码块的 HttpStatement。下面的可运行示例展示了如何以块(字节包)的形式接收响应内容并将其保存到文件:

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}")
    }

在此示例中,ByteReadChannel 用于异步读取数据。使用 ByteReadChannel.readRemaining() 会检索通道中所有可用的字节,而 Source.transferTo() 直接将数据写入文件,从而减少不必要的内存分配。

若要将响应体保存到文件而无需额外处理,你可以改为使用 ByteReadChannel.copyAndClose() 函数:

Kotlin
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}")
}