从 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.x | 2.0.0 |
|---|---|---|
| Locations | io.ktor:ktor-locations | io.ktor:ktor-server-locations |
| Webjars | io.ktor:ktor-webjars | io.ktor:ktor-server-webjars |
| AutoHeadResponse | io.ktor:ktor-server-core | io.ktor:ktor-server-auto-head-response |
| StatusPages | io.ktor:ktor-server-core | io.ktor:ktor-server-status-pages |
| CallId | io.ktor:ktor-server-core | io.ktor:ktor-server-call-id |
| DoubleReceive | io.ktor:ktor-server-core | io.ktor:ktor-server-double-receive |
| HTML DSL | io.ktor:ktor-html-builder | io.ktor:ktor-server-html-builder |
| FreeMarker | io.ktor:ktor-freemarker | io.ktor:ktor-server-freemarker |
| Velocity | io.ktor:ktor-velocity | io.ktor:ktor-server-velocity |
| Mustache | io.ktor:ktor-mustache | io.ktor:ktor-server-mustache |
| Thymeleaf | io.ktor:ktor-thymeleaf | io.ktor:ktor-server-thymeleaf |
| Pebble | io.ktor:ktor-pebble | io.ktor:ktor-server-pebble |
| kotlinx.serialization | io.ktor:ktor-serialization | io.ktor:ktor-server-content-negotiation, io.ktor:ktor-serialization-kotlinx-json |
| Gson | io.ktor:ktor-gson | io.ktor:ktor-server-content-negotiation, io.ktor:ktor-serialization-gson |
| Jackson | io.ktor:ktor-jackson | io.ktor:ktor-server-content-negotiation, io.ktor:ktor-serialization-jackson |
| Authentication | io.ktor:ktor-auth | io.ktor:ktor-server-auth |
| JWT authentication | io.ktor:ktor-auth-jwt | io.ktor:ktor-server-auth-jwt |
| LDAP authentication | io.ktor:ktor-auth-ldap | io.ktor:ktor-server-auth-ldap |
| DataConversion | io.ktor:ktor-server-core | io.ktor:ktor-server-data-conversion |
| DefaultHeaders | io.ktor:ktor-server-core | io.ktor:ktor-server-default-headers |
| Compression | io.ktor:ktor-server-core | io.ktor:ktor-server-compression |
| CachingHeaders | io.ktor:ktor-server-core | io.ktor:ktor-server-caching-headers |
| ConditionalHeaders | io.ktor:ktor-server-core | io.ktor:ktor-server-conditional-headers |
| CORS | io.ktor:ktor-server-core | io.ktor:ktor-server-cors |
| Forwarded headers | io.ktor:ktor-server-core | io.ktor:ktor-server-forwarded-header |
| HSTS | io.ktor:ktor-server-core | io.ktor:ktor-server-hsts |
| HttpsRedirect | io.ktor:ktor-server-core | io.ktor:ktor-server-http-redirect |
| PartialContent | io.ktor:ktor-server-core | io.ktor:ktor-server-partial-content |
| WebSockets | io.ktor:ktor-websockets | io.ktor:ktor-server-websockets |
| CallLogging | io.ktor:ktor-server-core | io.ktor:ktor-server-call-logging |
| Micrometer metric | io.ktor:ktor-metrics-micrometer | io.ktor:ktor-server-metrics-micrometer |
| Dropwizard metrics | io.ktor:ktor-metrics | io.ktor:ktor-server-metrics |
| Sessions | io.ktor:ktor-server-core | io.ktor:ktor-server-sessions |
要一次性添加所有插件,可以使用
io.ktor:ktor-server构件。
导入
| 子系统 | 1.6.x | 2.0.0 |
|---|---|---|
| Application | import io.ktor.application.* | import io.ktor.server.application.* |
| Configuration | import io.ktor.config.* | import io.ktor.server.config.* |
| Routing | import io.ktor.routing.* | import io.ktor.server.routing.* |
| AutoHeadResponse | import io.ktor.features.* | import io.ktor.server.plugins.autohead.* |
| StatusPages | import io.ktor.features.* | import io.ktor.server.plugins.statuspages.* |
| CallId | import io.ktor.features.* | import io.ktor.server.plugins.callid.* |
| DoubleReceive | import io.ktor.features.* | import io.ktor.server.plugins.doublereceive.* |
| Requests | import io.ktor.request.* | import io.ktor.server.request.* |
| Responses | import io.ktor.response.* | import io.ktor.server.response.* |
| Plugins | import io.ktor.features.* | import io.ktor.server.plugins.* |
| Locations | import io.ktor.locations.* | import io.ktor.server.locations.* |
| Static content | import io.ktor.http.content.* | import io.ktor.server.http.content.* |
| HTML DSL | import io.ktor.html.* | import io.ktor.server.html.* |
| FreeMarker | import io.ktor.freemarker.* | import io.ktor.server.freemarker.* |
| Velocity | import io.ktor.velocity.* | import io.ktor.server.velocity.* |
| Mustache | import io.ktor.mustache.* | import io.ktor.server.mustache.* |
| Thymeleaf | import io.ktor.thymeleaf.* | import io.ktor.server.thymeleaf.* |
| Pebble | import io.ktor.pebble.* | import io.ktor.server.pebble.* |
| ContentNegotiation | import io.ktor.features.* | import io.ktor.server.plugins.contentnegotiation.* |
| kotlinx.serialization | import io.ktor.serialization.* | import io.ktor.serialization.kotlinx.json.* |
| Gson | import io.ktor.gson.* | import io.ktor.serialization.gson.* |
| Jackson | import io.ktor.jackson.* | import io.ktor.serialization.jackson.* |
| Authentication | import io.ktor.auth.* | import io.ktor.server.auth.* |
| JWT authentication | import io.ktor.auth.jwt.* | import io.ktor.server.auth.jwt.* |
| LDAP authentication | import io.ktor.auth.ldap.* | import io.ktor.server.auth.ldap.* |
| Sessions | import io.ktor.sessions.* | import io.ktor.server.sessions.* |
| DefaultHeaders | import io.ktor.features.* | import io.ktor.server.plugins.defaultheaders.* |
| Compression | import io.ktor.features.* | import io.ktor.server.plugins.compression.* |
| CachingHeaders | import io.ktor.features.* | import io.ktor.server.plugins.cachingheaders.* |
| ConditionalHeaders | import io.ktor.features.* | import io.ktor.server.plugins.conditionalheaders.* |
| CORS | import io.ktor.features.* | import io.ktor.server.plugins.cors.* |
| Forwarded headers | import io.ktor.features.* | import io.ktor.server.plugins.forwardedheaders.* |
| HSTS | import io.ktor.features.* | import io.ktor.server.plugins.hsts.* |
| HttpsRedirect | import io.ktor.features.* | import io.ktor.server.plugins.httpsredirect.* |
| PartialContent | import io.ktor.features.* | import io.ktor.server.plugins.partialcontent.* |
| WebSockets | import io.ktor.websocket.* | import io.ktor.server.websocket.* |
| CallLogging | import io.ktor.features.* | import io.ktor.server.plugins.callloging.* |
| Micrometer metric | import io.ktor.metrics.micrometer.* | import io.ktor.server.metrics.micrometer.* |
| Dropwizard metrics | import io.ktor.metrics.dropwizard.* | import io.ktor.server.metrics.dropwizard.* |
WebSockets 代码已移至 'websockets' 软件包
WebSockets 代码已从 http-cio 移至 websockets 软件包。这需要按如下方式更新导入:
| 1.6.x | 2.0.0 |
|---|---|
import io.ktor.http.cio.websocket.* | import io.ktor.websocket.* |
请注意,此更改也会影响客户端。
Feature 已重命名为 Plugin
在 Ktor 2.0.0 中,Feature 已重命名为插件,以更好地描述拦截请求/响应流水线的功能 (KTOR-2326)。 这会影响整个 Ktor API,并要求按如下所述更新您的应用程序。
导入
安装任何插件都需要更新导入,同时也取决于是否将服务器代码移动到 io.ktor.server.* 软件包:
| 1.6.x | 2.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 概念(如流水线、阶段等)。相反,您可以使用各种处理程序(如
onCall、onCallReceive、onCallRespond等)访问处理请求和响应的不同阶段。您可以在此章节中了解流水线阶段如何映射到新 API 中的处理程序:将流水线阶段映射到新 API 处理程序。
内容协商与序列化
内容协商与序列化服务器 API 经过重构,以便在服务器和客户端之间复用序列化库。 主要更改包括:
ContentNegotiation已从ktor-server-core移至独立的ktor-server-content-negotiation构件。- 序列化库已从
ktor-*移至ktor-serialization-*构件,供客户端同时使用。
依赖项
| 子系统 | 1.6.x | 2.0.0 |
|---|---|---|
| ContentNegotiation | io.ktor:ktor-server-core | io.ktor:ktor-server-content-negotiation |
| kotlinx.serialization | io.ktor:ktor-serialization | io.ktor:ktor-serialization-kotlinx-json |
| Gson | io.ktor:ktor-gson | io.ktor:ktor-serialization-gson |
| Jackson | io.ktor:ktor-jackson | io.ktor:ktor-serialization-jackson |
导入
| 子系统 | 1.6.x | 2.0.0 |
|---|---|---|
| kotlinx.serialization | import io.ktor.serialization.* | import io.ktor.serialization.kotlinx.json.* |
| Gson | import io.ktor.gson.* | import io.ktor.serialization.gson.* |
| Jackson | import io.ktor.jackson.* | import io.ktor.serialization.jackson.* |
自定义转换器
ContentConverter 接口公开的函数签名按以下方式进行了更改:
interface ContentConverter {
suspend fun convertForSend(context: PipelineContext<Any, ApplicationCall>, contentType: ContentType, value: Any): Any?
suspend fun convertForReceive(context: PipelineContext<ApplicationReceiveRequest, ApplicationCall>): Any?
}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 客户端实例向服务器发出请求并验证结果。 - 要测试特定功能(例如 cookie 或 WebSockets),您需要创建一个新的客户端实例并安装相应的插件。
让我们来看几个将 1.6.x 测试迁移到 2.0.0 的示例:
基础服务器测试
在下面的测试中,handleRequest 函数被替换为 client.get 请求:
@Test
fun testRootLegacyApi() {
withTestApplication(Application::module) {
handleRequest(HttpMethod.Get, "/").apply {
assertEquals(HttpStatusCode.OK, response.status())
assertEquals("Hello, world!", response.content)
}
}
}@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 请求:
@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)
}
}@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 函数:
@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)
}
}@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 数据
在 v.1.6.x 中,您可以使用 kotlinx.serialization 库提供的 Json.encodeToString 函数序列化 JSON 数据。 在 v2.0.0 中,您需要创建一个新的客户端实例并安装 ContentNegotiation 插件,该插件允许以特定格式序列化/反序列化内容:
@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())
}
}@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 插件:
@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.")
}
} @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 对话:
@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)
}
}
} @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默认设置为true。receiveEntireContent属性已移除。
转发头
在 v2.0.0 中,ForwardedHeaderSupport 和 XForwardedHeaderSupport 插件分别重命名为 ForwardedHeaders 和 XForwardedHeaders。
缓存头
用于定义缓存选项的 options 函数现在除了接收 OutgoingContent 外,还接收 ApplicationCall 作为 Lambda 参数:
install(CachingHeaders) {
options { outgoingContent ->
// ...
}
}install(CachingHeaders) {
options { call, outgoingContent ->
// ...
}
}条件头
用于定义资源版本列表的 version 函数现在除了接收 OutgoingContent 外,还接收 ApplicationCall 作为 Lambda 参数:
install(ConditionalHeaders) {
version { outgoingContent ->
// ...
}
}install(ConditionalHeaders) {
version { call, outgoingContent ->
// ...
}
}CORS
CORS 配置中使用的几个函数已重命名:
host->allowHostheader->allowHeadermethod->allowMethod
install(CORS) {
host("0.0.0.0:5000")
header(HttpHeaders.ContentType)
method(HttpMethod.Options)
}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)。
请求函数
带多个参数的请求函数已被弃用。例如,port 和 path 参数需要替换为 HttpRequestBuilder 公开的 url 参数:
client.get(port = 8080, path = "/customer/3")client.get { url(port = 8080, path = "/customer/3") }HttpRequestBuilder 还允许您在请求函数 Lambda 内部指定额外的请求参数。
请求体
用于设置请求体的 HttpRequestBuilder.body 属性被替换为 HttpRequestBuilder.setBody 函数:
client.post("http://localhost:8080/post") {
body = "Body content"
}client.post("http://localhost:8080/post") {
setBody("Body content")
}响应
在 v2.0.0 中,请求函数(如 get、post、put、submitForm 等)不再接收用于接收特定类型对象的泛型参数。 现在所有请求函数都返回一个 HttpResponse 对象,该对象公开了带有泛型参数的 body 函数,用于接收特定类型的实例。 您还可以使用 bodyAsText 或 bodyAsChannel 将内容作为字符串或通道接收。
val httpResponse: HttpResponse = client.get("https://ktor.io/")
val stringBody: String = httpResponse.receive()
val byteArrayBody: ByteArray = httpResponse.receive()val httpResponse: HttpResponse = client.get("https://ktor.io/")
val stringBody: String = httpResponse.body()
val byteArrayBody: ByteArray = httpResponse.body()在安装了 ContentNegotiation 插件的情况下,您可以按如下方式接收任意对象:
val customer: Customer = client.get("http://localhost:8080/customer/3")val customer: Customer = client.get("http://localhost:8080/customer/3").body()流式响应
由于从请求函数中移除了泛型参数,接收流式响应需要单独的函数。 为了实现这一点,添加了带有 prepare 前缀的函数,如 prepareGet 或 preparePost:
public suspend fun HttpClient.prepareGet(builder: HttpRequestBuilder): HttpStatement
public suspend fun HttpClient.preparePost(builder: HttpRequestBuilder): HttpStatement以下示例演示了在这种情况下如何更改代码:
client.get<HttpStatement>("https://ktor.io/").execute { httpResponse ->
val channel: ByteReadChannel = httpResponse.receive()
while (!channel.isClosedForRead) {
// 读取数据
}
}client.prepareGet("https://ktor.io/").execute { httpResponse ->
val channel: ByteReadChannel = httpResponse.body()
while (!channel.isClosedForRead) {
// 读取数据
}
}您可以在此处找到完整示例:流式数据。
响应验证
在 v2.0.0 中,用于响应验证的 expectSuccess 属性默认设置为 false。 这需要在代码中进行以下更改:
- 要启用默认验证并针对非 2xx 响应抛出异常,请将
expectSuccess属性设置为true。 - 如果您使用
handleResponseExceptionWithRequest处理非 2xx 异常,还需要显式启用expectSuccess。
HttpResponseValidator
handleResponseException 函数被替换为 handleResponseExceptionWithRequest,后者增加了对 HttpRequest 的访问权限,以便在异常中提供额外信息:
HttpResponseValidator {
handleResponseException { exception ->
// ...
}
}HttpResponseValidator {
handleResponseExceptionWithRequest { exception, request ->
// ...
}
}内容协商与序列化
Ktor 客户端现在支持内容协商,并与 Ktor 服务器共享序列化库。 主要更改包括:
JsonFeature已弃用,改为使用ContentNegotiation,可在ktor-client-content-negotiation构件中找到。- 序列化库已从
ktor-client-*移至ktor-serialization-*构件。
依赖项
| 子系统 | 1.6.x | 2.0.0 |
|---|---|---|
ContentNegotiation | n/a | io.ktor:ktor-client-content-negotiation |
| kotlinx.serialization | io.ktor:ktor-client-serialization | io.ktor:ktor-serialization-kotlinx-json |
| Gson | io.ktor:ktor-client-gson | io.ktor:ktor-serialization-gson |
| Jackson | io.ktor:ktor-client-jackson | io.ktor:ktor-serialization-jackson |
导入
| 子系统 | 1.6.x | 2.0.0 |
|---|---|---|
ContentNegotiation | n/a | import io.ktor.client.plugins.contentnegotiation.* |
| kotlinx.serialization | import io.ktor.client.features.json.* | import io.ktor.serialization.kotlinx.json.* |
| Gson | import io.ktor.client.features.json.* | import io.ktor.serialization.gson.* |
| Jackson | import io.ktor.client.features.json.* | import io.ktor.serialization.jackson.* |
Bearer 身份验证
refreshTokens 函数现在使用 RefreshTokenParams 实例作为 Lambda 接收器 (this),而不是原来的 HttpResponse Lambda 参数 (it):
bearer {
refreshTokens { // it: HttpResponse
// ...
}
}bearer {
refreshTokens { // this: RefreshTokenParams
// ...
}
}RefreshTokenParams 公开了以下属性:
response用于访问响应参数;client用于发出刷新令牌的请求;oldTokens用于访问通过loadTokens获取的令牌。
HttpSend
HttpSend 插件的 API 已按如下方式更改:
client[HttpSend].intercept { originalCall, request ->
if (originalCall.something()) {
val newCall = execute(request)
// ...
}
}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 函数。
client.get(HttpSend).intercept { ... }
// 或
client[HttpSend].intercept { ... }client.plugin(HttpSend).intercept { ... }Feature 已重命名为 Plugin
与 Ktor 服务器一样,客户端 API 中的 Feature 已重命名为 Plugin。 这可能会影响您的应用程序,如下所述。
导入
更新用于安装插件的导入:
| 子系统 | 1.6.x | 2.0.0 |
| import io.ktor.client.features.* | import io.ktor.client.plugins.* |
身份验证 Auth 插件处理客户端应用程序中的身份验证和授权。 | 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 HttpCookies 插件自动处理 cookie,并在存储中的调用之间保留它们。 | import io.ktor.client.features.cookies.* | import io.ktor.client.plugins.cookies.* |
日志记录 所需依赖项:io.ktor:ktor-client-logging 代码示例:%example_name% | import io.ktor.client.features.logging.* | import io.ktor.client.plugins.logging.* |
WebSockets Websockets 插件允许您在服务器和客户端之间创建多向通信会话。 | import io.ktor.client.features.websocket.* | import io.ktor.client.plugins.websocket.* |
内容编码 ContentEncoding 插件允许您启用指定的压缩算法(如 'gzip' 和 'deflate')并配置其设置。 | import io.ktor.client.features.compression.* | import io.ktor.client.plugins.compression.* |
自定义插件
HttpClientFeature 接口已重命名为 HttpClientPlugin。
原生 (Native) 目标的新内存模型
在 v2.0.0 中,在 Native 目标上使用 Ktor 客户端需要启用新的 Kotlin/Native 内存模型:启用新内存模型 (MM)。
从 v2.2.0 开始,新的 Kotlin/Native 内存模型已默认启用。
'Ios' 引擎重命名为 'Darwin'
鉴于 Ios 引擎不仅针对 iOS,还针对包括 macOS 或 tvOS 在内的其他操作系统,在 v2.0.0 中,它已重命名为 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.x | 2.0.0 |
|---|---|
import io.ktor.http.cio.websocket.* | import io.ktor.websocket.* |
默认请求
DefaultRequest 插件使用 DefaultRequestBuilder 配置类,而不是 HttpRequestBuilder:
val client = HttpClient(CIO) {
defaultRequest {
// this: HttpRequestBuilder
}
}val client = HttpClient(CIO) {
defaultRequest {
// this: DefaultRequestBuilder
}
}