Kotlin logo

Kotlin

A concise multiplatform language developed by JetBrains

Ktor News

Ktor 3.0 现在推出新功能,性能也得到提升

Read this post in other languages:

此新版本使用 Kotlin 2.0,并切换到 kotlinx-io,因此 Ktor 更加现代化,与其他 Kotlin 工具的连接也更好。 Ktor 3.0 的运行速度更快,并提供了更多客户端-服务器应用程序构建选项。

如果您刚开始使用 Ktor 或需要了解其运作方式,请阅读我们最新发布的博文 Ktor 101:高效的 JVM HTTP 工具包,其中介绍了它的主要功能。

以下博文将为您全面概述 Ktor 3.0 中的最新变化和改进。

立即使用 Ktor 3.0 进行构建!

迁移到 kotlinx-io

此新版本中最大的更改是切换到基于 Okiokotlinx-io 库。 如果您使用 Ktor 的低级 IO API,此更改可能会影响您。 我们进行此更改是为了标准化各个 Kotlin 库的 IO 功能并提高性能,如我们的基准测试所示。

重大更改

更改主要影响低级 IO API,包括 InputOutputByteReadChannelByteWriteChannel 等类。 如果您直接使用这些类,将会看到弃用警告。 您应更新代码,以使用 kotlinx-io 库提供的替代代码。 别担心 – 在 4.0 版本发布之前,我们将一直支持旧 API,让您有充足的时间进行迁移。

新 kotlinx-io 库提供的多平台 API 可以处理各种数据源,并提供多种功能,包括处理文件、使用压缩等。 有关详情,请查阅 kotlinx-io API 文档

性能改进

切换到 kotlinx-io 后,我们减少了 ByteReadChannelByteWriteChannel 与网络接口之间不必要的字节复制。 这样可以更高效地进行字节转换和解析,为未来的性能改进留出了空间。

基于真实 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())
       }
   }
}

我们将示例分解为几个主要部分:

  1. remotePath – 基本 URL 路径,可以在该路径中访问 ZIP 内容。
  2. basePath – 您希望提供的 ZIP 文件内的基本路径。 在我们的示例中,假定 ZIP 归档包含 base 目录。 指定 basePath 内的所有路径均可在 remotePath/path/to/resource 中进行递归访问。 这意味着您可以使用子文件夹组织 ZIP 文件,这些子文件夹将反映在 URL 结构中。
  3. Path("files/text-files.zip") – 您要提供的 ZIP 文件的路径。
  4. default() 函数 – 用于指定在未请求特定文件时提供的默认文件。
  5. 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 构建新项目!

使用 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 升级的用户应参考详细的迁移指南,以确保顺利过渡至新版本。

更多文章和视频

  1. 📰 Ktor 2024 年路线图
  2. 📰 Ktor 插件注册表已发布!
  3. 📰 Ktor 101:高效的 JVM HTTP 工具包
  4. 🎥 使用 Ktor 管理复杂问题
  5. 🎥 扩展 Ktor:释放 Kotlin Web 框架的全部潜能

您的 Ktor 团队
JetBrains
The Drive to Develop

本博文英文原作者:

Anton Arhipov

Anton Arhipov

image description

Discover more