Kotlin
A concise multiplatform language developed by JetBrains
Ktor 3.0 现在推出新功能,性能也得到提升
此新版本使用 Kotlin 2.0,并切换到 kotlinx-io,因此 Ktor 更加现代化,与其他 Kotlin 工具的连接也更好。 Ktor 3.0 的运行速度更快,并提供了更多客户端-服务器应用程序构建选项。
如果您刚开始使用 Ktor 或需要了解其运作方式,请阅读我们最新发布的博文 Ktor 101:高效的 JVM HTTP 工具包,其中介绍了它的主要功能。
以下博文将为您全面概述 Ktor 3.0 中的最新变化和改进。
迁移到 kotlinx-io
此新版本中最大的更改是切换到基于 Okio 的 kotlinx-io 库。 如果您使用 Ktor 的低级 IO API,此更改可能会影响您。 我们进行此更改是为了标准化各个 Kotlin 库的 IO 功能并提高性能,如我们的基准测试所示。
重大更改
更改主要影响低级 IO API,包括 Input、Output、ByteReadChannel 和 ByteWriteChannel 等类。 如果您直接使用这些类,将会看到弃用警告。 您应更新代码,以使用 kotlinx-io 库提供的替代代码。 别担心 – 在 4.0 版本发布之前,我们将一直支持旧 API,让您有充足的时间进行迁移。
新 kotlinx-io 库提供的多平台 API 可以处理各种数据源,并提供多种功能,包括处理文件、使用压缩等。 有关详情,请查阅 kotlinx-io API 文档。
性能改进
切换到 kotlinx-io 后,我们减少了 ByteReadChannel
、ByteWriteChannel
与网络接口之间不必要的字节复制。 这样可以更高效地进行字节转换和解析,为未来的性能改进留出了空间。
基于真实 Ktor 应用程序的 IO 基准测试结果令人印象深刻。 一些测试显示,性能提高了 90% 以上,并且我们正着手实现更多增强功能。
支持服务器发送事件 (SSE)
在 Ktor 3.0 中,我们同时为服务器和客户端增加了对服务器发送事件 (SSE) 的初步支持。
利用服务器发送事件这项技术,服务器可以通过 HTTP 连接向客户端推送事件。 SSE 提供从服务器到客户端的单向通信通道。 如果服务器需要发送基于事件的更新,但不需要客户端反复轮询获取新信息,则非常适合使用这种方式。
要在 Ktor 应用程序中实现 SSE 支持,请先将 SSE 依赖项添加到您的项目的构建脚本中:
implementation("io.ktor:ktor-server-sse-jvm")
接下来,在您的 Ktor 应用程序中安装 SSE 插件,并使用 sse{}
函数创建端点:
import io.ktor.server.application.* import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.server.routing.* import io.ktor.server.sse.* import io.ktor.sse.* import kotlinx.coroutines.delay fun main() { embeddedServer(Netty, port = 8080) { install(SSE) routing { sse { repeat(42) { val name = call.parameters["name"] ?: "World" send(ServerSentEvent(data = "Hello, $name! $it")) delay(1000) } close() } } }.start(wait = true) }
在 sse {...}
块中,您可以通过以下功能访问 ServerSSESession
实例。
send()
:创建ServerSentEvent
并将其发送到客户端。call
:访问启动会话的关联ApplicationCall
。close()
:结束会话并终止与客户端的连接。
请注意,Ktor 目前不支持对 SSE 响应进行数据压缩。 如果您使用 Compression 插件,将默认跳过对 SSE 响应的压缩。
要使用 Ktor 客户端消费事件,在最简单的情况下,只需在客户端实例上调用 sse
函数即可,具体如下:
import io.ktor.client.* import io.ktor.client.engine.cio.* import io.ktor.client.plugins.sse.* import kotlinx.coroutines.runBlocking fun main() { val client = HttpClient(CIO) { install(SSE) } runBlocking { client.sse(host = "127.0.0.1", port = 8080, path = "/sse") { incoming.collect { event -> println(event) } } } }
在上例中,只要服务器在发送数据,客户端就会消费事件。
通过 ZIP 归档提供静态资源
利用新函数 staticZip
,用户可以提供 ZIP 归档的内容作为静态内容。
下面给出了该函数使用方法的基本示例:
routing { staticZip( remotePath = "/", basePath = "base", Path("files/text-files.zip") ) { default("file.txt") //modify the response by adding the HTTP Etag header modify { path, call -> call.response.headers.append(HttpHeaders.ETag, path.fileName.toString()) } } }
我们将示例分解为几个主要部分:
remotePath
– 基本 URL 路径,可以在该路径中访问 ZIP 内容。basePath
– 您希望提供的 ZIP 文件内的基本路径。 在我们的示例中,假定 ZIP 归档包含base
目录。 指定basePath
内的所有路径均可在remotePath/path/to/resource
中进行递归访问。 这意味着您可以使用子文件夹组织 ZIP 文件,这些子文件夹将反映在 URL 结构中。Path("files/text-files.zip")
– 您要提供的 ZIP 文件的路径。default()
函数 – 用于指定在未请求特定文件时提供的默认文件。modify
块 – 用于自定义响应。 在本例中,我们将基于文件名添加 ETag 标头。
访问 GitHub 上的 Ktor 示例仓库获取完整示例。
支持 CSRF
通过新插件增加了对 CSRF(跨站请求伪造)防护的支持。 为更改状态的操作(包括 POST、PUT 和 DELETE)启用了防护。
一般来讲,CSRF 防护仅适用于使用会话 Cookie 和表单的项目,因为启用 CSRF 防护会导致应用程序复杂性不必要地提高。
要在 Ktor 应用程序中启用 CSRF 支持,请先将以下依赖项添加到您的项目的构建脚本中:
implementation("io.ktor:ktor-server-csrf-jvm")
接下来,您可以为应用程序中的所选例程启用 CSRF 防护,如下所示:
route("/csrf") { install(CSRF) { allowOrigin("https://localhost:8080") originMatchesHost() checkHeader("X-CSRF") { csrfHeader -> request.headers[HttpHeaders.Origin]?.let { origin -> csrfHeader == origin.hashCode().toString(32) // 1ndrgg9 } == true } onFailure { respondText("Access denied!", status = HttpStatusCode.Forbidden) } } post { call.respondText("CSRF check was successful") } }
在配置块中,插件提供了几种方法来验证请求:
allowOrigin
指定仅允许来自预定义来源的请求。 在我们的示例中,预定义来源是https://localhost:8080
。originMatchesHost
强制要求请求的来源必须与应用程序的主机匹配。checkHeader
可以实现任意标头验证。
如果您使用以上代码运行应用程序,并使用 curl
命令向 /csrf
端点发送 POST
请求,您将看到以下结果:
curl -X POST -H "Content-Type: application/json" --data '{}' http://localhost:8080/csrf Access denied!
添加所需标头可使其通过检查:
curl -X POST -H "X-CSRF: 1ndrgg9" -H "Origin: http://localhost:8080" -H "Content-Type: application/json" --data '{}' http://localhost:8080/csrf CSRF check passed!
适用于 Wasm 的 Ktor 客户端
Ktor 客户端现在支持 WebAssembly (Wasm) 作为构建目标。 虽然 Kotlin/Wasm 仍处于早期阶段 (Alpha),但向 Ktor 客户端添加对 Wasm 的支持是扩展 Kotlin Multiplatform 生态系统的重要一步。 为了展示这一新功能,我们创建了一个示例 Compose Multiplatform 项目,该项目将 Ktor 客户端与 WebAssembly (wasmJs
) 目标结合使用。
要使用 Ktor 客户端,您需要将相应依赖项添加到项目的构建脚本中:
implementation("io.ktor:ktor-client-core:$ktor_version")
迁移指南
Ktor 3.0 带来了一些重要变化。 我们准备了迁移指南来帮助您轻松实现过渡。 在本文中,我们重点介绍最显著的变化。
无论您是否将现有项目升级到 Ktor 3.0,都可以立即开始使用 Ktor 3.0 构建新项目!
在 TestApplication
中显式加载模块
从现在起,TestApplication 类需要显式加载模块。 以下测试将启动一个空应用程序,且不会加载任何模块:
@Test fun testRoot() = testApplication {// TestApplication scope client.get("/").apply { assertEquals(HttpStatusCode.OK, status) assertEquals("Hello World!", bodyAsText()) } }
相反,您必须在 testApplication 函数中显式加载模块,或手动加载配置文件。
@Test fun testRoot() = testApplication { application { configureRouting() } client.get("/").apply { assertEquals(HttpStatusCode.OK, status) assertEquals("Hello World!", bodyAsText()) } }
插件更新
CallLogging
插件软件包已重命名,修正了一处拼写错误:
Locations
插件已被替换为新的 Resources
插件,用于实现类型安全路由。
这些变动旨在改善 Ktor 的架构、安全和开发者体验。 从 Ktor 2.x 升级的用户应参考详细的迁移指南,以确保顺利过渡至新版本。
更多文章和视频
- 📰 Ktor 2024 年路线图
- 📰 Ktor 插件注册表已发布!
- 📰 Ktor 101:高效的 JVM HTTP 工具包
- 🎥 使用 Ktor 管理复杂问题
- 🎥 扩展 Ktor:释放 Kotlin Web 框架的全部潜能
您的 Ktor 团队
JetBrains
The Drive to Develop
本博文英文原作者: