从 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.* |
插件 | 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
已重命名为Plugin,以更好地描述拦截请求/响应流水线的功能性 (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 客户端实例向服务器发出请求并验证结果。 - 要测试特定功能(例如,cookies 或 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 数据
在 v1.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
属性已移除。
Forwarded headers
在 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
->allowHost
header
->allowHeader
method
->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) {
// Read data
}
}
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
的访问,以在异常中提供额外信息:
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
用于发出请求以刷新 token;oldTokens
用于访问使用loadTokens
获取的 token。
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 { ... }
// or
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.*` |
认证 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.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
}
}