Ktor
Building Asynchronous Servers and Clients in Kotlin
Ktor 3.6.0 正式发布!
Ktor 3.6.0 来了!此版本包含多项全新实验性功能,包括类型化身份验证能力、针对 OpenID Connect 的专门支持,以及 Netty 引擎的 HTTP/3 支持。此外,路由与请求处理也迎来了一些易用性改进,Kotlin 多平台客户端获得了更便捷的默认配置,另有更多更新。完整变更列表请参阅我们网站上的 Ktor 3.6.0 新功能,或查看发布说明。
🚀 开始使用 Ktor 3.6.0
准备好探索 Ktor 3.6.0 了吗?使用 start.ktor.io 交互式项目生成器,开启你的下一个项目。我们始终欢迎你的反馈和贡献!
类型化身份验证
过去,Ktor 的身份验证依靠隐式类型将配置与路由关联起来。这个模块引入了新类型,确保在处理复杂身份验证时具备完整的类型安全;同时支持基于角色的访问控制和匿名用户。借助上下文参数,语法也更加简洁优雅。有关设置、角色检查和失败处理,请阅读类型安全身份验证文档。
val jwtAuth = jwt<User>("my-jwt") {
verifier(jwkProvider, issuer)
validate { credential ->
val payload = credential.payload
User(
id = payload.subject,
email = payload.getClaim("email").asString()
)
}
}
routing {
authenticateWith(jwtAuth) {
get("/profile") {
val user = call.principal
call.respondText(user)
}
}
}
OpenID Connect
全新的 OpenID Connect(Oidc)插件旨在降低通过 OpenID Connect 提供方保护服务时的复杂性。Oidc 插件允许你创建类型化身份验证提供方,并以类型安全的方式支持 OpenID Connect 的全部功能。此外还支持会话、具备令牌自动刷新功能的浏览器登录界面等。完整文档请查看 Ktor 网站。
suspend fun Application.module() {
val oidc = install(Oidc)
val auth0 = oidc.identityProvider("auth0") {
issuer = "https://my-tenant.auth0.com"
bearer {
audience = setOf("https://api.example.com")
}
}
routing {
authenticateWith(auth0.jwtBearer) {
get("/orders") {
val subject = call.principal.claims.subject
call.respondText("Hello $subject")
}
}
}
}
Netty 的更多功能
Netty 服务端引擎现已通过 QUIC 提供实验性 HTTP/3 支持。若要启用,请配置 SSL 连接器,然后通过 enableHttp3 { } 选择启用:
embeddedServer(Netty, environment, {
sslConnector(
keyStore = keyStore,
keyAlias = "server",
keyStorePassword = { "changeit".toCharArray() },
privateKeyPassword = { "changeit".toCharArray() }
) { port = 8443 }
enableHttp3 { quicMaxIdleTimeout = 30.seconds }
}) { /* application */ }.start(wait = true)
enableHttp3 {} 代码块还允许你调整 QUIC 专用设置,例如流量控制限制和 UDP 套接字配置。此功能仍处于实验阶段;如果你决定试用,欢迎反馈。
Netty 服务器现在还可以在一个连接器上提供 h2c,并在另一个连接器上通过 TLS 提供 HTTP/2。使用 enableH2c = true 和 enableHttp2 = true 可同时启用二者。
embeddedServer(Netty, configure = {
connector { port = 8080 }
sslConnector(...) { port = 8443 }
enableHttp2 = true
enableH2c = true
}) { /* application */ }.start(wait = true)
更高效的路由处理器
请求参数转换现已支持 Kotlin 的 Uuid、Byte 和无符号数值类型。ApplicationCall.receive() 现在也接受可空类型,使路由契约更加明确,同时弃用 receiveNullable()。
put("/users/{id}") {
val id: Uuid by call.parameters
val preferences = call.receive<NotificationPreferences?>()
if (preferences == null) {
preferenceService.clear(id)
} else {
preferenceService.update(id, preferences)
}
call.respond(HttpStatusCode.NoContent)
}
我们还新增了 respondHtmlPartial,用于取代已弃用的 respondHtmlFragment。新函数使用 TagConsumer<Appendable>,因此可以响应不受限制的局部 HTML,并支持 FlowContent 提供的全部元素。
get("/status") {
call.respondHtmlPartial(HttpStatusCode.OK) {
td { +"Ready" }
}
}
更灵活地控制 ContentNegotiation
客户端 ContentNegotiation 插件过去会把已注册的内容类型合并到每个 Accept 请求头中。这通常很有帮助,但如果 API 要求请求中显式设置的请求头保持原样,就并不合适。
使用 ContentTypeMergeStrategy.SkipIfPresent 时,显式 Accept 请求头优先。若请求没有 Accept 请求头,插件仍会像往常一样添加已注册的内容类型:
install(ContentNegotiation) {
register(ContentType.Application.Json, noOpJsonConverter)
acceptHeaderMergeStrategy = ContentTypeMergeStrategy.SkipIfPresent
}
简化 Kotlin 多平台客户端
Ktor 3.6.0 引入了 ktor-client-engine-defaults:一组为 Kotlin 多平台项目精选的客户端引擎。将其添加到 commonMain 后,即可在共享代码中创建 HttpClient(),无需选择引擎。Ktor 会为每个目标平台选择合适的可用引擎。
HTTP 缓存也采用了同样的思路。基于文件的缓存存储现在使用 kotlinx-io 的 Path,因此持久化 HttpCache 存储不再局限于 JVM 的 java.io.File API。结合这些改进,使用简单缓存配置 KMP 客户端变得轻松许多:
// build.gradle.kts
kotlin {
sourceSets {
commonMain {
dependencies {
api("io.ktor:ktor-client-engine-defaults:3.6.0")
}
}
}
}
// Main.kt
val client = HttpClient() {
install(HttpCache) {
publicStorage(FileStorage(Path("build/cache")))
}
}
这让 Ktor 项目拥有更自然的通用代码配置方式,同时仍可在某个平台需要时选择并配置特定引擎。
3.6.0 的完整变更列表还包括 JVM 的 WebRTC 支持、CIO 异步 DNS 解析、OpenAPI 标签描述、重复 Cookie 解析,以及 JavaScript fetch() 覆盖等内容。详情请参阅 Ktor 3.6.0 新功能。
🙏 感谢!
感谢社区中的每一位成员。你们的反馈、问题报告和贡献让每个 Ktor 版本都变得更好。特别感谢工作成果被纳入此版本的外部贡献者:kdelay、Rafa Ruiz 和 solo。
前往 start.ktor.io 开始构建你的下一个项目。我们始终欢迎你的建议和贡献!
👉 开始使用 Ktor | 💬 在 Slack 上加入社区
本博文英文原作者:Simon Vergauwen
本文由 AI 辅助翻译,并经人工审校。如发现任何问题,欢迎在评论区留言指正。