Echolot
教程

规范优先的设计与模拟

导入或设计 OpenAPI 规范,对其进行 lint,提供它的模拟服务,向模拟服务器发送请求,并对照规范检查真实响应。

规范优先意味着 OpenAPI 文档先于代码:先约定好 API,用模拟服务器试用它,等真实服务器出现后再对照同一份文档检查。在本教程中,你会把 Alpenglow 商店的规范添加到工作区,设计一个商店尚不具备的端点,在 Mac 上用模拟服务器同时提供两者,并对照规范检查真实 API 的响应。

  • 用时: 20 分钟。
  • 你需要: Echolot 和一个工作区,比如你的第一个工作区中的那个,或者一个新的空工作区。
  • 你将学到: “规范”页面、lint、遵循规范的请求、模拟服务器,以及 OpenAPI 规范检查。

1. 添加规范

演示商店在 https://demo.echolot.dev/openapi.yaml 发布了它的规范。

  1. 把它存到你的 Mac 上。在“终端”中:

    curl -o ~/Downloads/openapi.yaml https://demo.echolot.dev/openapi.yaml

    或者在 Safari 中打开该地址,然后选取文件 › 存储为…。

  2. 在工作区窗口中切换到规范(⌘3)。

  3. 选取添加规范文件…(在空白页面上,或边栏的 + 菜单中),然后选择 openapi.yaml。副本会放入工作区的 specs 文件夹。

规范会在可视化编辑器中打开:它的服务器、按路径或标签分组的端点,以及它的 Schema。工具栏上的开关可在可视化和代码之间切换。两者编辑的是同一份文本,存储的也是这份文本。

从零开始

请改为选取新建规范。你会得到一份标题为新 API 的 OpenAPI 3.1 文档,其中有一个端点 GET /items,可供重命名和扩展。本教程的其余部分操作方式相同。

2. 设计商店尚不具备的端点

商店想要一个门店列表。在有人动手实现之前,先把它设计出来。

  1. 在可视化编辑器中选取添加端点。

  2. 在新建端点中,保持方法为 GET,输入路径 /stores、摘要 List stores 和标签 Catalog。选取添加端点。Echolot 会添加该操作,并附带一个 200 响应作为起点。

  3. 在端点的编辑器中,把操作 ID 设为 listStores。

  4. 选取在代码中显示。把端点的 responses 替换为带有正文和示例的版本:

          responses:
            '200':
              description: The shop's stores.
              content:
                application/json:
                  schema:
                    type: array
                    items:
                      type: object
                      required: [id, city]
                      properties:
                        id: { type: string }
                        city: { type: string }
                        address: { type: string }
                  example:
                    - { id: zrh, city: Zürich, address: Bahnhofstrasse 1 }
                    - { id: brn, city: Bern, address: Spitalgasse 4 }

编辑会随着你的操作自动存储,⌘Z 可撤销。可视化编辑只会改动你所碰触条目的那几行,因此文件的其余部分(包括注释)保持你写下的样子。详见编辑规范。

可视化编辑器中的“规范”页面,选中了 Alpenglow 规范的一个端点

3. 对规范进行 lint

工具栏上的问题按钮会显示绿色对勾和没有问题,或者错误和警告的数量。

  1. 选取它。问题弹出窗口会说明文档符合 OpenAPI 规范,且所有引用都能解析。
  2. 制造一个问题来看看:在代码中,把某一行的 $ref: '#/components/schemas/Product' 改成 '#/components/schemas/Produkt'。按钮会变红。选取它并选中该问题:编辑器会跳到对应的行。
  3. 撤销这个更改(⌘Z)。

同样的 lint 也可以通过命令行工具在 CI 中运行。--strict 会在出现警告时同样失败,--hints 会加入建议,例如缺少操作 ID:

echolot spec lint specs/openapi.yaml --strict

4. 创建遵循规范的请求

  1. 在“规范”工具栏中选取生成请求。请求下会出现一个文件夹 Alpenglow demo shop,其中每个标签一个文件夹(Catalog、Auth、Orders),每个端点一个请求,包括 List stores。
  2. 查看 Catalog › List products。它的 URL 是 {{baseUrl}}/products。它的测试标签页已经带有规范生成的两项检查:状态等于 200,以及所记录正文的 JSON Schema。

这个文件夹会跟随规范。之后规范有变化时,“规范”工具栏的请求菜单会提供检查“Alpenglow demo shop”的更改…:它列出规范中新增、更改和删除的内容,并在不覆盖你对请求所做编辑的前提下应用它们。参见 OpenAPI 同步。

生成的请求以 {{baseUrl}}(小写的 rl)开头。你会在接下来的两个步骤中给它赋值。

5. 提供规范的模拟服务

  1. 切换到模拟服务器(⌘4)。打开 + 菜单并选取 Alpenglow demo shop 的模拟服务器。
  2. 选取开始(⌘R)。工具栏会显示绿点和地址 http://127.0.0.1:4010/v1:从 4010 起的第一个空闲端口,以及来自规范服务器的基础路径。
  3. 选取在环境中使用。Echolot 会创建一个环境 Mock: Alpenglow demo shop,其 {{baseUrl}} 就是模拟服务器的地址,并选中它。

设置卡片中有模拟服务器的端口、应答网络上的其他设备、每次应答前的延迟、一定比例的失败率(注入的 500 响应),以及检查:拒绝违反规范的请求(返回 400 及原因)。模拟服务器在窗口打开期间运行。

“模拟服务器”页面,Alpenglow demo shop 的模拟服务器运行在 127.0.0.1:4010,其中有它的设置和请求日志

6. 向模拟服务器发送请求

  1. 回到请求,发送 Catalog › List stores。模拟服务器返回 200 和你示例中的两家门店。标头标签页显示 Server: Echolot mock。
  2. 发送 Catalog › List products。模拟服务器没有它的示例,所以会根据 Schema 生成一个正文:形状正确,值是占位符。请求的两项检查都通过。
  3. 在 List products 的参数中,添加 sort,值为 cheapest,然后发送。模拟服务器会拒绝它:返回 400 和 query “sort”: not one of the allowed values。规范允许 name、price、-price 和 rating。移除这个参数。
  4. 在 Get a product 中添加标头 Prefer: code=404 并发送。模拟服务器会改为返回规范中记录的 404 问题响应。移除这个标头。

“模拟服务器”页面的日志列出了模拟服务器应答的每个请求,包括匹配到的操作和任何问题。在响应卡片中,你可以让某个操作对每个请求都返回另一个已记录的状态或示例。需要状态的应答可以使用 JavaScript 路由,参见脚本路由。

7. 对照规范检查真实 API

  1. 在环境页面添加环境 Demo,其中变量 baseUrl 设为 https://demo.echolot.dev/v1,然后在页面标题栏中选择它。
  2. 打开 List products,在它的测试标签页中打开添加测试并选取 OpenAPI 规范。它没有需要填写的内容:它会按规范为返回的状态记录的 Schema 来检查正文。
  3. 发送。三项检查全部通过:真实的商店与它的规范一致。
  4. 发送 List stores。真实的商店返回 404:这个端点只存在于规范中。这正是规范优先的意义:模拟服务器让你现在就能基于它开发。

要在 CI 中运行同样的检查,请用 echolot run 运行该文件夹,并在那里用 echolot mock 提供模拟服务:

echolot mock specs/openapi.yaml --port 4010

后续步骤

  • 从流量生成规范:根据你已经发送的请求编写规范。
  • 模拟服务器:从历史记录存储的示例、延迟、失败,以及在网络上共享。
  • echolot spec:文件夹落后于其规范时,check 会让 CI 失败。

本页内容