Skip to content

创建 RESTful API

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

代码示例 tutorial-server-restful-api

使用的插件

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

在本教程中,我们将说明如何使用 Kotlin 和 Ktor 构建后端服务,并以一个生成 JSON 文件的 RESTful API 为例。

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

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

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

前提条件

您可以独立完成本教程,但我们强烈建议您先完成上一篇教程,以学习如何

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

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

你好,RESTful 任务管理器

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

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

虽然您可以手动将其添加到现有项目中,但生成一个新项目然后逐步添加上一篇教程中的代码会更简单。您将在过程中重新编写所有代码,因此无需手头备有之前的项目。

  1. 导航至 Ktor 项目生成器

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

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

    1. Content Negotiation
    2. kotlinx.serialization
    3. Static Content

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

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

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

  2. 导航至 src/main/kotlin 并创建一个名为 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 数据

    显然,系统已经为我们完成了大量工作。这背后的机制究竟是什么?

理解内容协商

通过浏览器进行内容协商

当您创建项目时,已经包含了

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

在 HTTP 中,客户端通过 Accept 标头告知其可以渲染的内容类型。该标头的值是一个或多个内容类型。在上述示例中,您可以使用浏览器内置的开发者工具来检查此标头的值。

请看以下示例:

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

内容协商插件需要找到一种格式将数据发送回浏览器。如果您查看项目中生成的代码,会在 src/main/kotlin 内找到一个名为 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。您应该可以通过点击 View The Tasks 按钮来获取数据:

    显示按钮和任务以 HTML 表格形式展示的浏览器窗口

添加 GET 路由

现在您已经熟悉了内容协商的过程,请继续将

上一篇教程
通过构建任务管理器应用程序,学习在 Kotlin 中使用 Ktor 进行路由、处理请求和参数的基础知识。
中的功能转移到本教程中。

重用任务仓库

您可以无需任何修改地重用任务仓库,所以我们先来完成这一步。

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

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

    kotlin

重用 GET 请求的路由

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

  1. 导航至 src/main/kotlin 中的 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 优先级任务:

    显示 JSON 格式的中等优先级任务的浏览器窗口

    鉴于此类请求通常来自 JavaScript,因此采用更精细的测试方式更为理想。为此,您可以使用诸如 Postman 之类的专门工具。

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

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

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

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

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

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

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

    http
  3. 要在 IntelliJ IDEA 中发送请求,点击它旁边的装订区域图标(IntelliJ IDEA 装订区域图标)。

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

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

NOTE

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

为 POST 请求添加路由

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

  1. 打开 src/main/kotlin 内的 Routing.kt 文件。

  2. Application.configureRouting() 函数添加一个新的 POST 路由,如下所示:

    kotlin

    添加以下新导入:

    kotlin

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

  3. 重启应用程序。

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

  5. Body 面板中,添加以下代表新任务的 JSON 文档:

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

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

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

    http

添加删除支持

您即将完成向服务添加基础操作的工作。这些操作通常被简称为 CRUD(创建、读取、更新和删除)操作。现在您将实现删除操作。

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

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

    kotlin
  3. 重启应用程序。

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

    http
  5. 要在 IntelliJ IDEA 中发送 DELETE 请求,点击它旁边的装订区域图标(IntelliJ IDEA 装订区域图标)。

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

    HTTP 请求文件中的 DELETE 请求

使用 Ktor Client 创建单元测试

到目前为止,您已经手动测试了应用程序,但如您所见,这种方法耗时且无法扩展。相反,您可以实现

JUnit 测试
学习如何使用特殊的测试引擎来测试服务器应用程序。
,利用内置的 client 对象来获取并反序列化 JSON。

  1. 打开 src/test/kotlin 内的 ServerTest.kt 文件。

  2. ServerTest.kt 文件的内容替换为以下内容:

    kotlin

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

  3. build.gradle.kts 文件中添加以下依赖项:

    kotlin

使用 JsonPath 创建单元测试

使用 Ktor client 或类似库测试服务非常方便,但从质量保证 (QA) 的角度来看,它也有一个缺点。服务器由于不直接处理 JSON,无法确定其关于 JSON 结构的假设是否正确。

例如,此类假设包括:

  • 值存储在 array(数组)中,而实际上使用了 object(对象)。
  • 属性存储为 numbers(数字),而实际上是 strings(字符串)。
  • 成员按照声明顺序序列化,而实际上并非如此。

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

  1. 在您的 build.gradle.kts 文件的 dependencies 块中添加 JSONPath 库:

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

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

    kotlin

    JsonPath 查询的工作原理如下:

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

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

后续步骤

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

继续阅读

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