Skip to content

从 1.6.x 迁移到 2.0.x

本指南提供了关于如何将你的 Ktor 应用程序从 1.6.x 版本迁移到 2.0.x 版本的说明。

Ktor 服务器

服务器代码已移动到 'io.ktor.server.*' 包

为了统一并更好地区分服务器和客户端 API,服务器代码已移动到 io.ktor.server.* 包 (KTOR-2865)。 这意味着你需要更新应用程序中的依赖项导入,如下所示。

依赖项

子系统1.6.x2.0.0
Locationsio.ktor:ktor-locationsio.ktor:ktor-server-locations
Webjarsio.ktor:ktor-webjarsio.ktor:ktor-server-webjars
AutoHeadResponseio.ktor:ktor-server-coreio.ktor:ktor-server-auto-head-response
StatusPagesio.ktor:ktor-server-coreio.ktor:ktor-server-status-pages
CallIdio.ktor:ktor-server-coreio.ktor:ktor-server-call-id
DoubleReceiveio.ktor:ktor-server-coreio.ktor:ktor-server-double-receive
HTML DSLio.ktor:ktor-html-builderio.ktor:ktor-server-html-builder
FreeMarkerio.ktor:ktor-freemarkerio.ktor:ktor-server-freemarker
Velocityio.ktor:ktor-velocityio.ktor:ktor-server-velocity
Mustacheio.ktor:ktor-mustacheio.ktor:ktor-server-mustache
Thymeleafio.ktor:ktor-thymeleafio.ktor:ktor-server-thymeleaf
Pebbleio.ktor:ktor-pebbleio.ktor:ktor-server-pebble
kotlinx.serializationio.ktor:ktor-serializationio.ktor:ktor-server-content-negotiation, io.ktor:ktor-serialization-kotlinx-json
Gsonio.ktor:ktor-gsonio.ktor:ktor-server-content-negotiation, io.ktor:ktor-serialization-gson
Jacksonio.ktor:ktor-jacksonio.ktor:ktor-server-content-negotiation, io.ktor:ktor-serialization-jackson
Authenticationio.ktor:ktor-authio.ktor:ktor-server-auth
JWT authenticationio.ktor:ktor-auth-jwtio.ktor:ktor-server-auth-jwt
LDAP authenticationio.ktor:ktor-auth-ldapio.ktor:ktor-server-auth-ldap
DataConversionio.ktor:ktor-server-coreio.ktor:ktor-server-data-conversion
DefaultHeadersio.ktor:ktor-server-coreio.ktor:ktor-server-default-headers
Compressionio.ktor:ktor-server-coreio.ktor:ktor-server-compression
CachingHeadersio.ktor:ktor-server-coreio.ktor:ktor-server-caching-headers
ConditionalHeadersio.ktor:ktor-server-coreio.ktor:ktor-server-conditional-headers
CORSio.ktor:ktor-server-coreio.ktor:ktor-server-cors
Forwarded headersio.ktor:ktor-server-coreio.ktor:ktor-server-forwarded-header
HSTSio.ktor:ktor-server-coreio.ktor:ktor-server-hsts
HttpsRedirectio.ktor:ktor-server-coreio.ktor:ktor-server-http-redirect
PartialContentio.ktor:ktor-server-coreio.ktor:ktor-server-partial-content
WebSocketsio.ktor:ktor-websocketsio.ktor:ktor-server-websockets
CallLoggingio.ktor:ktor-server-coreio.ktor:ktor-server-call-logging
Micrometer metricio.ktor:ktor-metrics-micrometerio.ktor:ktor-server-metrics-micrometer
Dropwizard metricsio.ktor:ktor-metricsio.ktor:ktor-server-metrics
Sessionsio.ktor:ktor-server-coreio.ktor:ktor-server-sessions

要一次性添加所有插件,你可以使用 io.ktor:ktor-server 构件。

导入

子系统1.6.x2.0.0
Applicationimport io.ktor.application.*import io.ktor.server.application.*
Configurationimport io.ktor.config.*import io.ktor.server.config.*
Routingimport io.ktor.routing.*import io.ktor.server.routing.*
AutoHeadResponseimport io.ktor.features.*import io.ktor.server.plugins.autohead.*
StatusPagesimport io.ktor.features.*import io.ktor.server.plugins.statuspages.*
CallIdimport io.ktor.features.*import io.ktor.server.plugins.callid.*
DoubleReceiveimport io.ktor.features.*import io.ktor.server.plugins.doublereceive.*
Requestsimport io.ktor.request.*import io.ktor.server.request.*
Responsesimport io.ktor.response.*import io.ktor.server.response.*
插件import io.ktor.features.*import io.ktor.server.plugins.*
Locationsimport io.ktor.locations.*import io.ktor.server.locations.*
Static contentimport io.ktor.http.content.*import io.ktor.server.http.content.*
HTML DSLimport io.ktor.html.*import io.ktor.server.html.*
FreeMarkerimport io.ktor.freemarker.*import io.ktor.server.freemarker.*
Velocityimport io.ktor.velocity.*import io.ktor.server.velocity.*
Mustacheimport io.ktor.mustache.*import io.ktor.server.mustache.*
Thymeleafimport io.ktor.thymeleaf.*import io.ktor.server.thymeleaf.*
Pebbleimport io.ktor.pebble.*import io.ktor.server.pebble.*
ContentNegotiationimport io.ktor.features.*import io.ktor.server.plugins.contentnegotiation.*
kotlinx.serializationimport io.ktor.serialization.*import io.ktor.serialization.kotlinx.json.*
Gsonimport io.ktor.gson.*import io.ktor.serialization.gson.*
Jacksonimport io.ktor.jackson.*import io.ktor.serialization.jackson.*
Authenticationimport io.ktor.auth.*import io.ktor.server.auth.*
JWT authenticationimport io.ktor.auth.jwt.*import io.ktor.server.auth.jwt.*
LDAP authenticationimport io.ktor.auth.ldap.*import io.ktor.server.auth.ldap.*
Sessionsimport io.ktor.sessions.*import io.ktor.server.sessions.*
DefaultHeadersimport io.ktor.features.*import io.ktor.server.plugins.defaultheaders.*
Compressionimport io.ktor.features.*import io.ktor.server.plugins.compression.*
CachingHeadersimport io.ktor.features.*import io.ktor.server.plugins.cachingheaders.*
ConditionalHeadersimport io.ktor.features.*import io.ktor.server.plugins.conditionalheaders.*
CORSimport io.ktor.features.*import io.ktor.server.plugins.cors.*
Forwarded headersimport io.ktor.features.*import io.ktor.server.plugins.forwardedheaders.*
HSTSimport io.ktor.features.*import io.ktor.server.plugins.hsts.*
HttpsRedirectimport io.ktor.features.*import io.ktor.server.plugins.httpsredirect.*
PartialContentimport io.ktor.features.*import io.ktor.server.plugins.partialcontent.*
WebSocketsimport io.ktor.websocket.*import io.ktor.server.websocket.*
CallLoggingimport io.ktor.features.*import io.ktor.server.plugins.callloging.*
Micrometer metricimport io.ktor.metrics.micrometer.*import io.ktor.server.metrics.micrometer.*
Dropwizard metricsimport io.ktor.metrics.dropwizard.*import io.ktor.server.metrics.dropwizard.*

WebSockets 代码已移动到 'websockets' 包

WebSockets 代码已从 http-cio 移动到 websockets 包。这需要如下更新导入:

1.6.x2.0.0
import io.ktor.http.cio.websocket.*import io.ktor.websocket.*

请注意,此更改也影响客户端

Feature 已重命名为 Plugin

在 Ktor 2.0.0 中,Feature 已重命名为Plugin,以更好地描述拦截请求/响应流水线的功能性 (KTOR-2326)。 这影响了整个 Ktor API,需要你如下所述更新应用程序。

导入

安装任何插件都需要更新导入,并且还取决于服务器代码已移动到 io.ktor.server.* 包。

1.6.x2.0.0
import io.ktor.features.*import io.ktor.server.plugins.*

自定义插件

将 Feature 重命名为 Plugin 为自定义插件相关的 API 引入了以下更改:

  • ApplicationFeature 接口已重命名为 BaseApplicationPlugin
  • Features 流水线阶段已重命名为 Plugins

请注意,从 v2.0.0 开始,Ktor 提供了创建自定义插件的新 API。通常,此 API 不需要理解 Ktor 内部概念,例如流水线、阶段等等。取而代之的是,你可以使用各种处理程序(例如 onCallonCallReceiveonCallRespond 等等)访问处理请求和响应的不同阶段。你可以从本节了解流水线阶段如何映射到新 API 处理程序:流水线阶段到新 API 处理程序的映射

内容协商和序列化

内容协商和序列化服务器 API 已重构,以便在服务器和客户端之间重用序列化库。 主要更改是:

  • ContentNegotiation 已从 ktor-server-core 移动到一个单独的 ktor-server-content-negotiation 构件。
  • 序列化库已从 ktor-* 移动到客户端也使用的 ktor-serialization-* 构件。

你需要更新应用程序中的依赖项导入,如下所示。

依赖项

子系统1.6.x2.0.0
ContentNegotiationio.ktor:ktor-server-coreio.ktor:ktor-server-content-negotiation
kotlinx.serializationio.ktor:ktor-serializationio.ktor:ktor-serialization-kotlinx-json
Gsonio.ktor:ktor-gsonio.ktor:ktor-serialization-gson
Jacksonio.ktor:ktor-jacksonio.ktor:ktor-serialization-jackson

导入

子系统1.6.x2.0.0
kotlinx.serializationimport io.ktor.serialization.*import io.ktor.serialization.kotlinx.json.*
Gsonimport io.ktor.gson.*import io.ktor.serialization.gson.*
Jacksonimport io.ktor.jackson.*import io.ktor.serialization.jackson.*

自定义转换器

ContentConverter 接口暴露的函数签名已按以下方式更改:

kotlin
interface ContentConverter {
    suspend fun convertForSend(context: PipelineContext<Any, ApplicationCall>, contentType: ContentType, value: Any): Any?
    suspend fun convertForReceive(context: PipelineContext<ApplicationReceiveRequest, ApplicationCall>): Any?
}
kotlin
interface ContentConverter {
    suspend fun serialize(contentType: ContentType, charset: Charset, typeInfo: TypeInfo, value: Any): OutgoingContent?
    suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any?
}

测试 API

从 v2.0.0 开始,Ktor 服务器使用新的测试 API,解决了 KTOR-971 中描述的各种问题。主要更改是:

  • withTestApplication/withApplication 函数已替换为新的 testApplication 函数。
  • testApplication 函数内部,你需要使用现有的 Ktor 客户端实例向服务器发出请求并验证结果。
  • 要测试特定功能(例如,cookies 或 WebSockets),你需要创建一个新的客户端实例并安装相应的插件

让我们来看几个将 1.6.x 测试迁移到 2.0.0 的示例:

基本服务器测试

在下面的测试中,handleRequest 函数已替换为 client.get 请求:

kotlin
@Test
fun testRootLegacyApi() {
    withTestApplication(Application::module) {
        handleRequest(HttpMethod.Get, "/").apply {
            assertEquals(HttpStatusCode.OK, response.status())
            assertEquals("Hello, world!", response.content)
        }
    }
}
kotlin
@Test
fun testRoot() = testApplication {
    val response = client.get("/")
    assertEquals(HttpStatusCode.OK, response.status)
    assertEquals("Hello, world!", response.bodyAsText())
}

x-www-form-urlencoded

在下面的测试中,handleRequest 函数已替换为 client.post 请求:

kotlin
@Test
fun testPostLegacyApi() = withTestApplication(Application::main) {
    with(handleRequest(HttpMethod.Post, "/signup"){
        addHeader(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded.toString())
        setBody(listOf("username" to "JetBrains", "email" to "[email protected]", "password" to "foobar", "confirmation" to "foobar").formUrlEncode())
    }) {
        assertEquals("The 'JetBrains' account is created", response.content)
    }
}
kotlin
@Test
fun testPost() = testApplication {
    val response = client.post("/signup") {
        header(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded.toString())
        setBody(listOf("username" to "JetBrains", "email" to "[email protected]", "password" to "foobar", "confirmation" to "foobar").formUrlEncode())
    }
    assertEquals("The 'JetBrains' account is created", response.bodyAsText())
}

multipart/form-data

要在 v2.0.0 中构建 multipart/form-data,你需要将 MultiPartFormDataContent 传递给客户端的 setBody 函数:

kotlin
    @Test
    fun testUploadLegacyApi() = withTestApplication(Application::main) {
        with(handleRequest(HttpMethod.Post, "/upload"){
            val boundary = "WebAppBoundary"
            val fileBytes = File("ktor_logo.png").readBytes()

            addHeader(HttpHeaders.ContentType, ContentType.MultiPart.FormData.withParameter("boundary", boundary).toString())
            setBody(boundary, listOf(
                PartData.FormItem("Ktor logo", { }, headersOf(
                    HttpHeaders.ContentDisposition,
                    ContentDisposition.Inline
                        .withParameter(ContentDisposition.Parameters.Name, "description")
                        .toString()
                )),
                PartData.FileItem({ fileBytes.inputStream().asInput() }, {}, headersOf(
                    HttpHeaders.ContentDisposition,
                    ContentDisposition.File
                        .withParameter(ContentDisposition.Parameters.Name, "image")
                        .withParameter(ContentDisposition.Parameters.FileName, "ktor_logo.png")
                        .toString()
                ))
            ))
        }) {
            assertEquals("Ktor logo is uploaded to 'uploads/ktor_logo.png'", response.content)
        }
    }
kotlin
@Test
fun testUpload() = testApplication {
    val boundary = "WebAppBoundary"
    val response = client.post("/upload") {
        setBody(
            MultiPartFormDataContent(
                formData {
                    append("description", "Ktor logo")
                    append("image", File("ktor_logo.png").readBytes(), Headers.build {
                        append(HttpHeaders.ContentType, "image/png")
                        append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"")
                    })
                },
                boundary,
                ContentType.MultiPart.FormData.withParameter("boundary", boundary)
            )
        )
    }
    assertEquals("Ktor logo is uploaded to 'uploads/ktor_logo.png'", response.bodyAsText())
}

JSON 数据

在 v1.6.x 中,你可以使用 kotlinx.serialization 库提供的 Json.encodeToString 函数序列化 JSON 数据。 从 v2.0.0 开始,你需要创建一个新的客户端实例并安装 ContentNegotiation 插件,该插件允许以特定格式序列化/反序列化内容:

kotlin
@Test
fun testPostCustomerLegacyApi() = withTestApplication(Application::main) {
    with(handleRequest(HttpMethod.Post, "/customer"){
        addHeader(HttpHeaders.ContentType, ContentType.Application.Json.toString())
        setBody(Json.encodeToString(Customer(3, "Jet", "Brains")))
    }) {
        assertEquals("Customer stored correctly", response.content)
        assertEquals(HttpStatusCode.Created, response.status())
    }
}
kotlin
@Test
fun testPostCustomer() = testApplication {
    val client = createClient {
        install(ContentNegotiation) {
            json()
        }
    }
    val response = client.post("/customer") {
        contentType(ContentType.Application.Json)
        setBody(Customer(3, "Jet", "Brains"))
    }
    assertEquals("Customer stored correctly", response.bodyAsText())
    assertEquals(HttpStatusCode.Created, response.status)
}

测试期间保留 cookie

在 v1.6.x 中,cookiesSession 用于在测试时保留请求之间的 cookie。从 v2.0.0 开始,你需要创建一个新的客户端实例并安装 HttpCookies 插件:

kotlin
    @Test
    fun testRequestsLegacyApi() = withTestApplication(Application::main) {
        fun doRequestAndCheckResponse(path: String, expected: String) {
            handleRequest(HttpMethod.Get, path).apply {
                assertEquals(expected, response.content)
            }
        }

        cookiesSession {
            handleRequest(HttpMethod.Get, "/login") {}.apply {}
            doRequestAndCheckResponse("/user", "Session ID is 123abc. Reload count is 0.")
            doRequestAndCheckResponse("/user", "Session ID is 123abc. Reload count is 1.")
            doRequestAndCheckResponse("/user", "Session ID is 123abc. Reload count is 2.")

            handleRequest(HttpMethod.Get, "/logout").apply {}
            doRequestAndCheckResponse("/user", "Session doesn't exist or is expired.")
        }
    }
kotlin
    @Test
    fun testRequests() = testApplication {
        val client = createClient {
            install(HttpCookies)
        }

        val loginResponse = client.get("/login")
        val response1 = client.get("/user")
        assertEquals("Session ID is 123abc. Reload count is 1.", response1.bodyAsText())
        val response2 = client.get("/user")
        assertEquals("Session ID is 123abc. Reload count is 2.", response2.bodyAsText())
        val response3 = client.get("/user")
        assertEquals("Session ID is 123abc. Reload count is 3.", response3.bodyAsText())
        val logoutResponse = client.get("/logout")
        assertEquals("Session doesn't exist or is expired.", logoutResponse.bodyAsText())
    }

WebSockets

在旧 API 中,handleWebSocketConversation 用于测试 WebSocket 会话。从 v2.0.0 开始,你可以通过使用客户端提供的 WebSockets 插件来测试 WebSocket 会话:

kotlin
    @Test
    fun testConversationLegacyApi() {
        withTestApplication(Application::module) {
            handleWebSocketConversation("/echo") { incoming, outgoing ->
                val greetingText = (incoming.receive() as Frame.Text).readText()
                assertEquals("Please enter your name", greetingText)

                outgoing.send(Frame.Text("JetBrains"))
                val responseText = (incoming.receive() as Frame.Text).readText()
                assertEquals("Hi, JetBrains!", responseText)
            }
        }
    }
kotlin
    @Test
    fun testConversation() {
        testApplication {
            val client = createClient {
                install(WebSockets)
            }

            client.webSocket("/echo") {
                val greetingText = (incoming.receive() as? Frame.Text)?.readText() ?: ""
                assertEquals("Please enter your name", greetingText)

                send(Frame.Text("JetBrains"))
                val responseText = (incoming.receive() as Frame.Text).readText()
                assertEquals("Hi, JetBrains!", responseText)
            }
        }
    }

DoubleReceive

从 v2.0.0 开始,DoubleReceive 插件配置引入了 cacheRawRequest 属性,它与 receiveEntireContent 相反:

  • 在 v1.6.x 中,receiveEntireContent 属性默认设置为 false
  • 在 v2.0.0 中,cacheRawRequest 默认设置为 truereceiveEntireContent 属性已移除。

Forwarded headers

在 v2.0.0 中,ForwardedHeaderSupportXForwardedHeaderSupport 插件分别重命名为 ForwardedHeadersXForwardedHeaders

缓存头

用于定义缓存选项的 options 函数现在除了接受 OutgoingContent 之外,还接受 ApplicationCall 作为 lambda 实参:

kotlin
install(CachingHeaders) {
    options { outgoingContent ->
        // ...
    }
}
kotlin
install(CachingHeaders) {
    options { call, outgoingContent ->
        // ...
    }
}

条件头

用于定义资源版本列表的 version 函数现在除了接受 OutgoingContent 之外,还接受 ApplicationCall 作为 lambda 实参:

kotlin
install(ConditionalHeaders) {
    version { outgoingContent ->
        // ... 
    }
}
kotlin
install(ConditionalHeaders) {
    version { call, outgoingContent ->
        // ... 
    }
}

CORS

CORS 配置中使用的几个函数已重命名:

  • host -> allowHost
  • header -> allowHeader
  • method -> allowMethod
kotlin
install(CORS) {
    host("0.0.0.0:5000")
    header(HttpHeaders.ContentType)
    method(HttpMethod.Options)
}
kotlin
install(CORS) {
    allowHost("0.0.0.0:5000")
    allowHeader(HttpHeaders.ContentType)
    allowMethod(HttpMethod.Options)
}

MicrometerMetrics

在 v1.6.x 中,baseName 属性用于指定用于监控 HTTP 请求的 Ktor 指标的基本名称(前缀)。 默认情况下,它等于 ktor.http.server。 从 v2.0.0 开始,baseName 已替换为 metricName,其默认值为 ktor.http.server.requests

Ktor 客户端

请求和响应

在 v2.0.0 中,用于发出请求和接收响应的 API 已更新,使其更一致、更易发现 (KTOR-29)。

请求函数

带有多个形参的请求函数已弃用。例如,portpath 形参需要替换为 HttpRequestBuilder 暴露的 url 形参:

kotlin
client.get(port = 8080, path = "/customer/3")
kotlin
client.get { url(port = 8080, path = "/customer/3") }

HttpRequestBuilder 还允许你在请求函数 lambda 内部指定额外的请求形参

请求体

用于设置请求体HttpRequestBuilder.body 属性已替换为 HttpRequestBuilder.setBody 函数:

kotlin
client.post("http://localhost:8080/post") {
    body = "Body content"
}
kotlin
client.post("http://localhost:8080/post") {
    setBody("Body content")
}

响应

从 v2.0.0 开始,请求函数(例如 getpostputsubmitForm 等等)不接受用于接收特定类型对象的泛型实参。 现在所有请求函数都返回一个 HttpResponse 对象,该对象暴露了带有泛型实参的 body 函数,用于接收特定类型实例。 你也可以使用 bodyAsTextbodyAsChannel 将内容接收为字符串或通道。

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

安装 ContentNegotiation 插件后,你可以如下所示接收任意对象:

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

流式响应

由于请求函数中移除了泛型实参,因此接收流式响应需要单独的函数。 为此,已添加带有 prepare 前缀的函数,例如 prepareGetpreparePost

kotlin
public suspend fun HttpClient.prepareGet(builder: HttpRequestBuilder): HttpStatement
public suspend fun HttpClient.preparePost(builder: HttpRequestBuilder): HttpStatement

下面的示例展示了在这种情况下如何更改你的代码:

kotlin
client.get<HttpStatement>("https://ktor.io/").execute { httpResponse ->
    val channel: ByteReadChannel = httpResponse.receive()
    while (!channel.isClosedForRead) {
        // Read data
    }
}
kotlin
client.prepareGet("https://ktor.io/").execute { httpResponse ->
    val channel: ByteReadChannel = httpResponse.body()
    while (!channel.isClosedForRead) {
        // Read data
    }
}

你可以在此处找到完整示例:流式数据

响应验证

从 v2.0.0 开始,用于响应验证expectSuccess 属性默认设置为 false。 这需要在你的代码中进行以下更改:

  • 启用默认验证并对非 2xx 响应抛出异常,请将 expectSuccess 属性设置为 true
  • 如果你处理非 2xx 异常时使用了 handleResponseExceptionWithRequest,则也需要显式地启用 expectSuccess

HttpResponseValidator

handleResponseException 函数已替换为 handleResponseExceptionWithRequest,后者增加了对 HttpRequest 的访问,以在异常中提供额外信息:

kotlin
HttpResponseValidator {
    handleResponseException { exception ->
        // ...
    }
}
kotlin
HttpResponseValidator {
    handleResponseExceptionWithRequest { exception, request ->
        // ...
    }
}

内容协商和序列化

Ktor 客户端现在支持内容协商,并与 Ktor 服务器共享序列化库。 主要更改是:

  • JsonFeature 已弃用,取而代之的是 ContentNegotiation,后者可以在 ktor-client-content-negotiation 构件中找到。
  • 序列化库已从 ktor-client-* 移动到 ktor-serialization-* 构件。

你需要更新客户端代码中的依赖项导入,如下所示。

依赖项

子系统1.6.x2.0.0
ContentNegotiationn/aio.ktor:ktor-client-content-negotiation
kotlinx.serializationio.ktor:ktor-client-serializationio.ktor:ktor-serialization-kotlinx-json
Gsonio.ktor:ktor-client-gsonio.ktor:ktor-serialization-gson
Jacksonio.ktor:ktor-client-jacksonio.ktor:ktor-serialization-jackson

导入

子系统1.6.x2.0.0
ContentNegotiationn/aimport io.ktor.client.plugins.contentnegotiation.*
kotlinx.serializationimport io.ktor.client.features.json.*import io.ktor.serialization.kotlinx.json.*
Gsonimport io.ktor.client.features.json.*import io.ktor.serialization.gson.*
Jacksonimport io.ktor.client.features.json.*import io.ktor.serialization.jackson.*

Bearer 认证

refreshTokens 函数现在使用 RefreshTokenParams 实例作为 lambda 接收者 (this),而不是 HttpResponse lambda 实参 (it):

kotlin
bearer {
    refreshTokens {  // it: HttpResponse
        // ...
    }
}
kotlin
bearer {
    refreshTokens { // this: RefreshTokenParams
        // ...
    }
}

RefreshTokenParams 暴露了以下属性:

  • response 用于访问响应形参;
  • client 用于发出请求以刷新 token;
  • oldTokens 用于访问使用 loadTokens 获取的 token。

HttpSend

HttpSend 插件的 API 更改如下:

kotlin
client[HttpSend].intercept { originalCall, request ->
    if (originalCall.something()) {
        val newCall = execute(request)
        // ...
    }
}
kotlin
client.plugin(HttpSend).intercept { request ->
    val originalCall = execute(request)
    if (originalCall.something()) {
        val newCall = execute(request)
        // ...
    }
}

请注意,在 v2.0.0 中,索引访问不可用于访问插件。请改用 HttpClient.plugin 函数。

HttpClient.get(plugin: HttpClientPlugin) 函数已移除

在 2.0.0 版本中,接受客户端插件的 HttpClient.get 函数已移除。请改用 HttpClient.plugin 函数。

kotlin
client.get(HttpSend).intercept { ... }
// or
client[HttpSend].intercept { ... }
kotlin
client.plugin(HttpSend).intercept { ... }

Feature 已重命名为 Plugin

至于 Ktor 服务器,客户端 API 中的 Feature 已重命名为 Plugin。 这可能会影响你的应用程序,如下所述。

导入

更新安装插件的导入:

子系统1.6.x2.0.0
  • 默认请求
    The DefaultRequest plugin allows you to configure default parameters for all requests.
  • 用户代理
    undefined
  • 字符集
    undefined
  • 响应验证
    Learn how to validate a response depending on its status code.
  • 超时
    Code example: %example_name%
  • HttpCache
    The HttpCache plugin allows you to save previously fetched resources in an in-memory or persistent cache.
  • HttpSend
    Code example: %example_name%
`import io.ktor.client.features.*``import io.ktor.client.plugins.*`
认证
The Auth plugin handles authentication and authorization in your client application.
` import io.ktor.client.features.auth.* ` ` import io.ktor.client.features.auth.providers.* ` ` import io.ktor.client.plugins.auth.* ` ` import io.ktor.client.plugins.auth.providers.* `
Cookie
The HttpCookies plugin handles cookies automatically and keep them between calls in a storage.
`import io.ktor.client.features.cookies.*``import io.ktor.client.plugins.cookies.*`
日志
Required dependencies: io.ktor:ktor-client-logging Code example: %example_name%
`import io.ktor.client.features.logging.*``import io.ktor.client.plugins.logging.*`
WebSockets
The Websockets plugin allows you to create a multi-way communication session between a server and a client.
`import io.ktor.client.features.websocket.*``import io.ktor.client.plugins.websocket.*`
内容编码
The ContentEncoding plugin allows you to enable specified compression algorithms (such as 'gzip' and 'deflate') and configure their settings.
`import io.ktor.client.features.compression.*``import io.ktor.client.plugins.compression.*`

自定义插件

HttpClientFeature 接口已重命名为 HttpClientPlugin

Native 目标的全新内存模型

从 v2.0.0 开始,在 Native 目标上使用 Ktor 客户端需要启用全新的 Kotlin/Native 内存模型:启用新内存模型

从 v2.2.0 开始,全新的 Kotlin/Native 内存模型默认启用

'Ios' 引擎已重命名为 'Darwin'

鉴于 Ios 引擎在 v2.0.0 中不仅面向 iOS,还面向其他操作系统,包括 macOS 或 tvOS,因此它已重命名为 Darwin。这导致了以下更改:

  • io.ktor:ktor-client-ios 构件已重命名为 io.ktor:ktor-client-darwin
  • 要创建 HttpClient 实例,你需要将 Darwin 类作为实参传递。
  • IosClientEngineConfig 配置类已重命名为 DarwinClientEngineConfig

要了解如何配置 Darwin 引擎,请参见 Darwin 部分。

WebSockets 代码已移动到 'websockets' 包

WebSockets 代码已从 http-cio 移动到 websockets 包。这需要如下更新导入:

1.6.x2.0.0
import io.ktor.http.cio.websocket.*import io.ktor.websocket.*

默认请求

DefaultRequest 插件使用 DefaultRequestBuilder 配置类而不是 HttpRequestBuilder

kotlin
val client = HttpClient(CIO) {
    defaultRequest {
        // this: HttpRequestBuilder
    }
}
kotlin
val client = HttpClient(CIO) {
    defaultRequest {
        // this: DefaultRequestBuilder
    }
}