Skip to content

如何在 Kotlin 中使用 Ktor 创建 RESTful API

代码示例: tutorial-server-restful-api

使用的插件:

Routing
路由是服务器应用程序中用于处理传入请求的核心插件。
,
Static Content
了解如何提供静态内容,例如样式表、脚本、图像等。
,
Content Negotiation
ContentNegotiation 插件主要有两个目的:协商客户端和服务器之间的媒体类型,以及以特定格式序列化/反序列化内容。
, kotlinx.serialization

在本教程中,我们将解释如何使用 Kotlin 和 Ktor 构建后端服务,其中包括一个生成 JSON 文件的 RESTful API 示例。

之前的教程
了解如何使用 Ktor 在 Kotlin 中构建任务管理器应用程序,学习路由、处理请求和参数的基础知识。
中,我们向你介绍了验证、错误处理和单元测试的基础知识。本教程将通过创建一个用于管理任务的 RESTful 服务来扩展这些主题。

你将学习如何执行以下操作:

  • 创建使用 JSON 序列化的 RESTful 服务。
  • 理解
    内容协商
    ContentNegotiation 插件主要有两个目的:协商客户端和服务器之间的媒体类型,以及以特定格式序列化/反序列化内容。
    的过程。
  • 在 Ktor 中定义 REST API 的路由。

先决条件

你可以独立完成本教程, 但是,我们强烈建议你完成之前的教程,了解如何

处理请求和生成响应
了解如何使用 Ktor 在 Kotlin 中构建任务管理器应用程序,学习路由、处理请求和参数的基础知识。

我们建议你安装 IntelliJ IDEA,但你也可以使用你选择的其他 IDE。

Hello RESTful 任务管理器

在本教程中,你将把现有的任务管理器重写为 RESTful 服务。为此,你将使用多个 Ktor

插件
插件提供通用功能,例如序列化、内容编码、压缩等。

虽然你可以手动将其添加到现有项目中,但更简单的方法是生成一个新项目,然后逐步添加之前教程中的代码。你将在过程中重复所有的代码,因此你不需要手头有之前的项目。

  1. 导航到 Ktor 项目生成器

  2. 项目构件 字段中,输入 com.example.ktor-rest-task-app 作为你的项目构件名称。 在 Ktor 项目生成器中命名项目构件

  3. 在插件部分,通过点击 添加 按钮搜索并添加以下插件:

    1. Routing
    2. Content Negotiation
    3. Kotlinx.serialization
    4. Static Content

    在 Ktor 项目生成器中添加插件 添加插件后,你将看到所有 四个插件列在项目设置下方。 Ktor 项目生成器中的插件列表

  4. 点击 下载 按钮以生成并下载你的 Ktor 项目。

  1. 在 IntelliJ IDEA 中打开你的项目,如在 IntelliJ IDEA 中打开、探索并运行你的 Ktor 项目教程中所述。

  2. 导航到 src/main/kotlin/com/example 并创建一个名为 model 的子包。

  3. model包内,创建一个新的 Task.kt 文件。

  4. 打开 Task.kt 文件,添加一个 enum 来表示优先级,以及一个 class 来表示任务:

    kotlin

    在之前的教程中,你使用了扩展函数将 Task 转换为 HTML。在本例中, Task 类使用kotlinx.serialization库中的Serializable 类型进行了注解。

  5. 打开 Routing.kt 文件,并用以下实现替换现有代码:

    kotlin

    与之前的教程类似,你为 URL /tasks 的 GET 请求创建了一个路由。 这次,你不再手动转换任务列表,而是直接返回该列表。

  6. 在 IntelliJ IDEA 中,点击运行按钮 (intelliJ IDEA 运行图标) 来启动应用程序。

  7. 在浏览器中导航到 http://0.0.0.0:8080/tasks。你应该会看到任务列表的 JSON 版本,如下所示:

  8. 浏览器屏幕中显示的 JSON 数据

    显然,有很多工作正在替我们完成。到底发生了什么?

理解内容协商

通过浏览器进行内容协商

创建项目时,你包含了

Content Negotiation
ContentNegotiation 插件主要有两个目的:协商客户端和服务器之间的媒体类型,以及以特定格式序列化/反序列化内容。
插件。该插件会查看客户端可以渲染的内容类型,并将其与当前服务可以提供的内容类型进行匹配。因此,得名

在 HTTP 中,客户端通过 Accept 标头表明它可以渲染哪些内容类型。此标头的值是一个或多个内容类型。在上述情况下,你可以使用浏览器内置的开发工具来检查此标头的值。

考虑以下示例:

请注意包含 /。此标头表示它接受 HTML、XML 或图像——但它也会接受任何其他内容 类型。

Content Negotiation 插件需要找到一种格式将数据发送回浏览器。如果你查看项目中的生成代码,你会发现src/main/kotlin/com/example目录下有一个名为Serialization.kt的文件,其中包含以下内容:

kotlin

此代码安装了 ContentNegotiation 插件,并配置了 kotlinx.serialization 插件。这样,当客户端发送请求时,服务器可以发送回序列化为 JSON 的对象。

在来自浏览器的请求中,ContentNegotiation 插件知道它只能返回 JSON,并且浏览器会尝试显示它收到的任何内容。因此请求成功。

    在生产环境中,你不会希望直接在浏览器中显示 JSON。相反,会有 JavaScript 代码在浏览器中运行,它会发出请求,然后将返回的数据作为单页应用 (SPA) 的一部分显示出来。通常,这类应用会使用诸如 ReactAngularVue.js 等框架编写。

  1. 为了模拟这一点,打开 src/main/resources/static 目录下的 index.html 页面,并用以下内容替换默认内容:

    html

    此页面包含一个 HTML 表单和一个空表。提交表单后,JavaScript 事件处理程序会向 /tasks 端点发送请求,并将 Accept 标头设置为 application/json。返回的数据随后被反序列化并添加到 HTML 表格中。

  2. 在 IntelliJ IDEA 中,点击重新运行按钮 (intelliJ IDEA 重新运行图标) 来重新启动应用程序。

  3. 导航到 URL http://0.0.0.0:8080/static/index.html。 你应该能够通过点击 查看任务 按钮来获取数据:

    显示按钮和 HTML 表格中显示任务的浏览器窗口

添加 GET 路由

既然你已经熟悉了内容协商的过程,接下来请继续将

上一个教程
了解如何使用 Ktor 在 Kotlin 中构建任务管理器应用程序,学习路由、处理请求和参数的基础知识。
中的功能迁移到本教程中。

重用任务仓库

你可以不加修改地重用任务仓库,所以我们先来做这件事。

  1. model包内,创建一个新的 TaskRepository.kt 文件。

  2. 打开 TaskRepository.kt 并添加以下代码:

    kotlin

重用 GET 请求的路由

现在你已经创建了仓库,可以实现 GET 请求的路由了。之前的代码可以简化,因为你不再需要担心将任务转换为 HTML:

  1. 导航到src/main/kotlin/com/example目录下的 Routing.kt 文件。

  2. 使用以下实现更新Application.configureRouting()函数中/tasks路由的代码:

    kotlin

    有了这个,你的服务器可以响应以下 GET 请求:

    • /tasks 返回仓库中的所有任务。
    • /tasks/byName/{taskName} 返回按指定 taskName 过滤的任务。
    • /tasks/byPriority/{priority} 返回按指定 priority 过滤的任务。
  3. 在 IntelliJ IDEA 中,点击重新运行按钮 (intelliJ IDEA 重新运行图标) 来重新启动应用程序。

测试功能

    你可以在浏览器中测试这些路由。例如,导航到 http://0.0.0.0:8080/tasks/byPriority/Medium 以 JSON 格式查看所有具有 Medium 优先级的任务:

    显示具有 Medium 优先级任务的 JSON 格式的浏览器窗口

    鉴于这些类型的请求通常来自 JavaScript,更精细的测试是更优的选择。为此,你可以使用像 Postman 这样的专业工具。

  1. 在 Postman 中,创建一个新的 GET 请求,URL 为 http://0.0.0.0:8080/tasks/byPriority/Medium

  2. Headers 窗格中,将 Accept 标头的值设置为 application/json

  3. 点击 发送 以发送请求并在响应查看器中查看响应。

    Postman 中显示具有 Medium 优先级任务的 JSON 格式的 GET 请求

    在 IntelliJ IDEA Ultimate 中,你可以在 HTTP 请求文件中执行相同的步骤。

  1. 在项目根目录中,创建一个新的 REST Task Manager.http 文件。

  2. 打开 REST Task Manager.http 文件并添加以下 GET 请求:

    http
  3. 要在 IntelliJ IDE 中发送请求,请点击其旁边的边栏图标 (intelliJ IDEA 边栏图标)。

  4. 这将在 Services 工具窗口中打开并运行:

    HTTP 文件中显示具有 Medium 优先级任务的 JSON 格式的 GET 请求

NOTE

另一种测试路由的方法是在 Kotlin Notebook 中使用 khttp 库。

为 POST 请求添加路由

在之前的教程中,任务是通过 HTML 表单创建的。然而,由于你现在正在构建一个 RESTful 服务,你不再需要那样做。相反,你将利用 kotlinx.serialization 框架,它将完成大部分繁重的工作。

  1. 打开 src/main/kotlin/com/example 目录下的 Routing.kt 文件。

  2. 按如下方式向Application.configureRouting()函数添加一个新的 POST 路由:

    kotlin

    添加以下新的导入:

    kotlin

    当 POST 请求发送到 /tasks 时,kotlinx.serialization 框架用于将请求正文转换为 Task 对象。如果成功,任务将被添加到仓库。如果反序列化过程失败,服务器将需要处理 SerializationException,而如果任务重复,则需要处理 IllegalStateException

  3. 重新启动应用程序。

  4. 要在 Postman 中测试此功能,创建一个新的 POST 请求,URL 为 http://0.0.0.0:8080/tasks

  5. Body 窗格中,添加以下 JSON 文档以表示新任务:

    json
    Postman 中用于添加新任务的 POST 请求
  6. 点击 发送 以发送请求。

  7. 你可以通过向 http://0.0.0.0:8080/tasks 发送 GET 请求来验证任务是否已添加。

  8. 在 IntelliJ IDEA Ultimate 中,你可以通过将以下内容添加到你的 HTTP 请求文件来执行相同的步骤:

    http

添加移除支持

你已接近完成向服务添加基本操作。这些操作通常被概括为 CRUD 操作——即创建(Create)、读取(Read)、更新(Update)和删除(Delete)的缩写。现在你将实现删除操作。

  1. TaskRepository.kt 文件中,在 TaskRepository 对象中添加以下方法,以根据任务名称移除任务:

    kotlin
  2. 打开 Routing.kt 文件,并在 routing() 函数中添加一个端点来处理 DELETE 请求:

    kotlin
  3. 重新启动应用程序。

  4. 将以下 DELETE 请求添加到你的 HTTP 请求文件:

    http
  5. 要在 IntelliJ IDE 中发送 DELETE 请求,请点击其旁边的边栏图标 (intelliJ IDEA 边栏图标)。

  6. 你将在 Services 工具窗口中看到响应:

    HTTP 请求文件中的 DELETE 请求

使用 Ktor 客户端创建单元测试

到目前为止,你都是手动测试应用程序,但正如你已经注意到的那样,这种方法耗时且无法扩展。相反,你可以实现

JUnit 测试
了解如何使用专用测试引擎测试你的服务器应用程序。
,使用内置的 client 对象来获取和反序列化 JSON。

  1. 打开 src/test/kotlin/com/example 目录下的 ApplicationTest.kt 文件。

  2. 用以下内容替换 ApplicationTest.kt 文件的内容:

    kotlin

    请注意,你需要将 ContentNegotiationkotlinx.serialization 插件安装到插件中,与你在服务器上安装的方式相同。

  3. 将以下依赖项添加到位于 gradle/libs.versions.toml 的版本目录中:

    yaml
  4. 将新依赖项添加到你的 build.gradle.kts 文件:

    kotlin

使用 JsonPath 创建单元测试

使用 Ktor 客户端或类似库测试你的服务很方便,但从质量保证 (QA) 的角度来看,它有一个缺点。服务器不直接处理 JSON,因此无法确定其对 JSON 结构的假设。

例如,假设如下:

  • 值存储在 array 中,而实际上使用了 object
  • 属性存储为 numbers,而实际上它们是 strings
  • 成员按声明顺序序列化,而实际上并非如此。

如果你的服务旨在供多个客户端使用,那么对 JSON 结构有信心至关重要。为了实现这一点,请使用 Ktor 客户端从服务器检索文本,然后使用 JSONPath 库分析此内容。

  1. 在你的 build.gradle.kts 文件中,将 JSONPath 库添加到 dependencies 代码块:

    kotlin
  2. 导航到 src/test/kotlin/com/example 文件夹并创建一个新的 ApplicationJsonPathTest.kt 文件。

  3. 打开 ApplicationJsonPathTest.kt 文件并添加以下内容:

    kotlin

    JsonPath 查询的工作方式如下:

    • $[*].name 表示“将文档视为一个数组,并返回每个条目的 name 属性值”。
    • $[?(@.priority == '$priority')].name 表示“返回数组中每个 priority 等于所提供值的条目的 name 属性值”。

    你可以使用这些查询来确认你对返回 JSON 的理解。当你进行代码重构和服务重新部署时,即使当前的框架不会中断反序列化,序列化中的任何修改也会被识别出来。这让你能够放心地重新发布公共可用的 API。

后续步骤

恭喜!你现在已经完成了为你的任务管理器应用程序创建 RESTful API 服务,并学习了使用 Ktor 客户端和 JsonPath 进行单元测试的要点。

继续学习

下一个教程
了解如何使用 Kotlin、Ktor 和 Thymeleaf 模板构建网站。
,了解如何重用你的 API 服务来构建一个 Web 应用程序。