1. 概述

Ktor 是基于 Kotlin 语言开发的现代异步服务框架,专为构建高性能的客户端与服务器应用而设计。它支持嵌入式服务器部署,无需依赖外部容器即可独立运行。

本文将带你使用 Ktor 快速搭建一个轻量级、可扩展的 Kotlin 后端服务,并涵盖路由、JSON 序列化、中间件安装等核心功能。对于熟悉 JVM 生态但想尝试更简洁 Web 框架的开发者来说,Ktor 是个不错的选择 —— 尤其适合微服务或 API 网关场景。

✅ 特点总结:

  • 基于协程(Coroutine),天然支持非阻塞 I/O
  • 模块化设计,按需启用功能(称为“特性” Feature)
  • 支持多种服务器引擎:Netty、Jetty、CIO 等
  • 与 Kotlin DSL 高度融合,代码直观易读

2. 项目初始化

推荐使用 Gradle 初始化 Ktor 项目。Gradle 安装可参考官网指南:https://gradle.org/install/

创建 build.gradle 文件并配置如下依赖:

val ktorVersion = "2.3.11"
val logbackVersion = "1.4.14"
val kotlinTestUnit = "1.9.10"

plugins {
    kotlin("jvm") version "1.9.10"
    id("io.ktor.plugin") version "2.3.5"
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("io.ktor", "ktor-server-netty", ktorVersion)
    implementation("io.ktor", "ktor-server-content-negotiation", ktorVersion)
    implementation("io.ktor", "ktor-server-default-headers", ktorVersion) 
    implementation("io.ktor", "ktor-server-call-logging", ktorVersion)
    implementation("ch.qos.logback", "logback-classic", logbackVersion)
}

kotlin {
    jvmToolchain(20)
}

task runServer(type: JavaExec) {
    mainClass = 'APIServer'
    classpath = sourceSets.main.runtimeClasspath
}

⚠️ 注意:

  • 使用了 ktor-server-netty 作为嵌入式服务器
  • ktor-server-content-negotiation 是后续处理 JSON 所必需的模块
  • 已设置 JVM 20 工具链,确保兼容最新 Kotlin 协程特性

3. 启动服务实例

src/main/kotlin 目录下创建主类文件 APIServer.kt

fun main(args: Array<String>) {
    embeddedServer(Netty, port = 8080) {
        // 路由和功能配置将在这里添加
    }.start(wait = true)
}

这段代码会启动一个基于 Netty 的服务监听在 8080 端口。
wait = true 表示主线程阻塞等待请求,避免程序立即退出。

💡 小贴士:生产环境建议通过信号量控制生命周期,而不是用 wait=true


4. 定义第一个接口

要处理 HTTP 请求,需要启用 Ktor 的 Routing 功能。这是所有接口定义的基础。

修改 embeddedServer 块:

embeddedServer(Netty, 8080) {
    install(Routing) {
        get("/todo") {
            val jsonResponse = """{
                "id": 1,
                "task": "Pay waterbill",
                "description": "Pay water bill today"
            }"""
            call.respondText(jsonResponse, ContentType.Application.Json)
        }
    }
}.start(wait = true)

现在访问 http://localhost:8080/todo 就能收到静态 JSON 响应。

❌ 踩坑提醒:注意 JSON 字符串中不要有多余逗号(如最后一个字段后加 ,),否则前端解析可能失败。


5. 运行服务

我们在 build.gradle 中已定义运行任务:

task runServer(type: JavaExec) {
    mainClass = 'APIServer'
    classpath = sourceSets.main.runtimeClasspath
}

执行命令启动服务:

./gradlew runServer

服务启动成功后,可通过浏览器或 curl 测试:

curl http://localhost:8080/todo

预期返回之前定义的 todo 数据。


6. 安装特性(Features)

Ktor 的核心理念是“插件化”。每个功能以 Feature 形式存在,通过 install() 注入到请求处理管道中。

常见的内置特性包括:

  • DefaultHeaders:为每个响应自动添加指定头信息
  • CallLogging:记录每次请求日志
  • Compression:开启 GZIP 压缩
  • StatusPages:统一异常响应格式

示例:添加自定义响应头

install(DefaultHeaders) {
    header("X-Developer", "Baeldung")
    header(HttpHeaders.Server, "My Server")
}

这样所有响应都会带上这两个头部。你可以用 Chrome DevTools 或 Postman 查看验证。

📌 技术细节:HttpHeaders 是 Ktor 提供的标准头常量集合,比如 HttpHeaders.ContentTypeHttpHeaders.CacheControl 等,建议优先使用这些常量而非硬编码字符串。


7. JSON 自动序列化

手动拼接 JSON 字符串不仅容易出错,也不利于维护。Ktor 提供了内容协商机制(Content Negotiation)配合 Gson 实现对象 ↔ JSON 自动转换。

添加依赖

确保 build.gradle 包含以下条目:

implementation("io.ktor", "ktor-server-content-negotiation", ktorVersion)
implementation("io.ktor", "ktor-serialization-gson", ktorVersion)

配置 Gson 序列化器

install(ContentNegotiation) {
    gson {
        setPrettyPrinting() // 格式化输出,便于调试
    }
}

返回数据对象

定义一个简单的数据类:

data class Author(val name: String, val website: String)

然后注册接口:

get("/author") {
    val author = Author("baeldung", "baeldung.com")
    call.respond(author)
}

此时访问 /author 接口,Ktor 会自动将其序列化为 JSON 并设置正确的 Content-Type: application/json

✅ 效果对比:

  • 旧方式:手写 JSON 字符串 → 易错、难维护
  • 新方式:直接 call.respond(dataObject) → 清爽又安全

8. 实现 CRUD 控制器

下面我们构建一个简易的 Todo 管理系统,演示如何组织多个接口逻辑。

定义模型类

data class ToDo(
    var id: Int,
    val name: String,
    val description: String,
    val completed: Boolean
)

创建内存存储

val toDoList = ArrayList<ToDo>()

⚠️ 注意:这里仅用于演示。实际项目应使用数据库(如 JPA、Exposed)或缓存层。

注册 RESTful 接口

routing {
    route("/todo") {
        post {
            val toDo = call.receive<ToDo>()
            toDo.id = toDoList.size
            toDoList.add(toDo)
            call.respond("Added")
        }

        delete("/{id}") {
            val id = call.parameters["id"]!!.toInt()
            if (id < 0 || id >= toDoList.size) call.respond(HttpStatusCode.NotFound)
            else call.respond(toDoList.removeAt(id))
        }

        get("/{id}") {
            val id = call.parameters["id"]!!.toInt()
            if (id < 0 || id >= toDoList.size) call.respond(HttpStatusCode.NotFound)
            else call.respond(toDoList[id])
        }

        get {
            call.respond(toDoList)
        }
    }
}

✅ 方法映射说明:

HTTP 方法 路径 功能
GET /todo 获取全部任务
GET /todo/{id} 查询单个任务
POST /todo 添加新任务
DELETE /todo/{id} 删除指定任务

⚠️ 踩坑提醒:

  • call.receive<T>() 会抛异常,建议包裹 try-catch 或使用 validation pipeline
  • 参数为空时 call.parameters["id"] 返回 null,务必判空或使用 !! 明确断言(仅限测试环境)

9. 总结

我们完成了一个完整的 Kotlin + Ktor 后端原型开发流程:

  • 使用 Gradle 快速搭建工程
  • 嵌入 Netty 服务器实现零部署运行
  • 利用 Routing 构建清晰的接口结构
  • 通过 ContentNegotiation 实现 JSON 自动编解码
  • 展示了 Feature 机制的灵活性和可扩展性

整个过程几乎没有样板代码,充分体现了 Kotlin DSL 和协程在 Web 开发中的优势。

🌐 完整源码地址:https://github.com/Baeldung/kotlin-tutorials/tree/master/kotlin-ktor

如果你正在寻找 Spring Boot 的轻量替代方案,或者希望尝试函数式风格的服务编写方式,Ktor 绝对值得一试。尤其适合快速构建内部工具、API 中间层或 Prototyping 阶段的产品验证。


原始标题:Kotlin with Ktor

« 上一篇: Kotlin 反射详解