本章内容包括:
- 使用代码规范检查(linting)确保你的 API 符合风格指南
- 编写自定义的 API 代码规范检查规则
- 创建 AI 辅助的代码规范检查规则
在第1章中,我介绍了 APIOps 及其优势,第2章则提供了一些概念工具,帮助你推进 APIOps 转型。在本章,你将迈出应用 APIOps 的第一步,进行更实操的内容。本章将介绍如何运行 API 代码规范检查规则,以确认你的 API 是否符合 API 风格指南。此章节适合设计和实现 API 的开发人员阅读,API 产品负责人和产品经理也能从中了解 API 代码规范检查的工作原理。
我会演示如何在 API 设计编辑器和命令行中运行 API 代码规范检查,并教你如何创建简单的 API 检查规则,同时展示 AI 如何助力创建检查规则。
请按照附录 B 中的说明安装本章所需工具:Spectral CLI、Visual Studio Code (VS Code)、OpenAPI (Swagger) Editor VS Code 插件、yq、jpp 以及 GitHub Copilot。要跟着本章的练习操作,请克隆本书的代码仓库 github.com/apiopsbook/... ,并进入 chapter3 目录。如果你只想查看完成的练习文件,可以进入 chapter3-completed 文件夹。
3.1 APIOps 中的 API 代码规范检查
在 API 产品的设计阶段,你需要确保 API 设计符合 API 风格指南。API 风格指南是一个参考文档,包含组织认可的 API 命名规范、分页模式、错误格式、路径模式及其他设计约定,用于帮助组织规范 API。API 标准化非常重要,因为一致性能够提升 API 的可用性、采纳率和安全性。API 风格指南也被称为 API 标准或 API 设计指南,是 API 治理的重要组成部分。
通常,API 风格指南会描述 API 设计必须遵守的规则。例如,某些风格指南会规定 URI 路径必须使用 CamelCase,大多数 API 认证应采用 OAuth2,错误响应应包含消息和标识码等。拥有成熟 API 项目的组织通常会有自己的 API 风格指南,而刚开始做 API 的团队可以参考公开的风格指南并进行调整,形成适合自己的指南。
常见的 API 风格指南示例
以下是一些流行的公开 REST API 风格指南:
- Zalando RESTful API and Event Guidelines:mng.bz/aE9z
- Microsoft REST API Guidelines:mng.bz/d6KN
- Haufe-Lexware API Style Guide:mng.bz/rV4B
- Belgian Interoperability Framework REST Guidelines:mng.bz/Vx4P
- 按设计主题分类的公开 API 风格指南汇总(由 Arnaud Lauret 维护):apistylebook.com
API 代码规范检查是对 API 定义文件进行自动化分析,检查其是否符合 API 风格指南。它类似于静态代码分析,但作用于 API 定义文件。API 代码规范检查工具能快速自动反馈你的 API 设计是否符合组织 API 风格指南文档中的规则。相比开发者必须阅读和记忆冗长的 API 标准文档,这种自动化检查方式更有效。
在 API 产品生命周期的设计阶段,API 设计师会在本地通过 IDE 或命令行运行代码规范检查,同时在持续集成构建中对提交的 API 定义文件进行检查。图 3.1 展示了这一过程。

3.2 API 设计合规性问题
假设某开发团队被要求设计一个符合 JSON:API(jsonapi.org)标准的-o68ft29i3gv/) API。JSON:API 规范用于构建 REST API,明确规定了客户端如何请求资源的获取或修改,以及服务器应如何响应这些请求。JSON:API 是一个详细规范,包含了 REST API JSON 响应的格式、错误信息的规范、请求和响应使用的媒体类型等诸多具体规则。
REST、JSON:API 与 OAS
REST(表现层状态转移)是 Roy Fielding 发明的一种 Web 服务架构风格。Fielding 指出,遵循此风格的 API 应该是无状态的,客户端与服务器解耦,拥有统一接口,支持缓存,并且能提供按需代码。RESTful API 允许用户通过统一资源定位符(URL)和操作(HTTP 动词,如 GET、POST、PATCH)对服务器上的资源执行操作。
在本书中描述 REST API 时,我使用以下术语:
- 资源(Resource):具有类型、关联数据(也称属性)和与其他资源关系的对象。例如请求
GET /users/1234
,即获取标识为 1234 的用户资源。 - 集合(Collections):同质资源的集合,同时集合本身也是资源。例如请求
GET /users
获取用户集合。 - 子资源(Subresource):处于父资源层级之下的资源,存在父子关系。例如请求
GET /users/1234/article/9876
,获取位于用户 1234 之下的文章 9876 子资源。
OpenAPI 规范(OAS)是一种描述任意 HTTP Web API(无论是否 RESTful)的格式,使用 YAML 或 JSON 文档描述 API。而 JSON:API 是一个针对 REST API 设计的具体规范。符合 JSON:API 指南的 API 也可以用 OAS 文档表示。
团队已经学习了 JSON:API 规范,并基于其指南设计了 API。他们认为自己的设计是合规的,但不确定。现在你被要求验证他们的 API 设计是否符合 JSON:API 指南。你如何用 API 代码规范检查工具(API linter)来验证他们是否遵守了标准?
注意:APIOps 的重点是确保 API 设计人员产出的设计与 API 风格指南保持一致。虽然本书提及了 API 设计的某些方面,但不详述具体设计内容。关于 API 设计,推荐 James Higginbotham 的《Principles of Web API Design》(Addison-Wesley Professional, 2021)和 Arnaud Lauret 的《The Design of Web APIs》(Manning, 2019),这两本书对 API 设计技巧有详尽讲解。
当然,你也可以不用 API linter,而是手动阅读完整的 JSON:API 规范,逐条审查团队 API 定义文件中的请求与响应是否符合标准。但 JSON:API 规范规则繁多,容易遗漏细节。所以我们来看如何用 API linter 来完成这一验证。
3.3 介绍 API 代码规范检查工具(API linters)
API linting 工具有两个输入:需要检查的 API 定义文件,以及一组用于检查的规则集(ruleset)。运行时,API linter 会输出发现的问题,指出 API 定义文件中的具体行号和违反的规则。API linter 可以集成在 API 设计编辑器中,也可以作为独立命令行工具运行。
最流行的开源 OpenAPI linter 是 Stoplight 的 Spectral(github.com/stoplightio...),支持 JSON 和 YAML 文件的检查。它已集成到 Stoplight Studio、Postman、Insomnia 等设计和协作工具中,也有 IntelliJ IDEA、VS Code 等多种 IDE 和编辑器的插件。
除此之外,Redocly CLI(github.com/Redocly/red...)也提供易用的 OpenAPI 文件管理和代码规范检查功能。Vacuum(github.com/daveshanley...)是一款速度极快的 OpenAPI linter,兼容 Spectral 规则集。还有 Kevin Swiber 的 Spego(github.com/kevinswiber...),它基于 Open Policy Agent(OPA)使用声明式策略语言 Rego 来定义规则,验证 OpenAPI 定义。
本章使用 Spectral CLI 作为示例,演示如何在命令行运行 API linting,并展示如何在开发者常用的 VS Code 编辑器中使用 Spectral 插件获取代码规范检查报告。
小型组织的 API 风格指南问题?
刚起步的小型组织可能还没有准备好完整的 API 风格指南。如果没有风格指南,是否还需要使用 API linting 来检查一致性呢?
准备 API 风格指南需要时间(网络上有许多优秀示例,但通常需要结合实际调整),但在你定义自定义规则之前,API linting 依然可以帮你检查 OpenAPI 定义文件中的一些内容。比如,验证定义文件的格式是否有效,这对于生成 API 文档、共享给 API 使用者或作为其他工具的输入都很重要。
另外,Spectral 和 Redocly CLI 自带一套基础规则,用于检测大多数 API 定义文件的基本一致性,例如检查示例对象是否符合声明的模式等。
对于只有一个或少数几个开发团队的小型组织,我推荐尽早使用 API linting,启用自带的基础规则,随着风格指南逐步成型,再逐步添加自定义规则。
3.4 使用 Spectral 检查合规性
基于 3.2 节中的场景,你可以使用 Spectral CLI 针对团队的 API 定义文件和 JSON:API 规则集运行检查。规则集是一组用 JSON、YAML 或 JavaScript 编写的规则,基于你的 API 风格指南。
在你克隆的示例项目的 chapter3 目录下,有一个名为 json-api.ruleset.yaml
的文件,包含 JSON:API 标准的规则。还有一个 product-catalog.oas.yaml
文件,是团队在假设场景中创建的 API 定义文件。
用 verbose 选项运行 Spectral lint 命令:
css
$ spectral lint product-catalog.oas.yaml \
--ruleset json-api.ruleset.yaml --verbose
你应该看到如下输出:
bash
Found 23 rules (23 enabled)
Linting /Users/ikenna/chapter2/product-catalog.oas.yaml
No results with a severity of 'error' found!
这意味着 Spectral 已成功对 API 定义文件进行规则检查,未发现任何问题。团队的 API 定义符合规则集中的所有规则,你无需自己读懂 JSON:API 规范也能完成检查。
如果 API 设计违反了某条规则,目录下另一个文件 product-catalog-v2.oas.yaml
就存在违规示例,你可以用以下命令运行 Spectral 检查:
css
$ spectral lint product-catalog-v2.oas.yaml \
--ruleset json-api.ruleset.yaml --verbose
输出类似如下:
bash
Found 23 rules (23 enabled)
Linting /Users/ikenna/chapter3/product-catalog-v2.oas.yaml
/Users/ikenna/chapter3/product-catalog-v2.oas.yaml
41:30 error response-content-type All JSON:API response bodies MUST
↪ be returned with the header Content-Type: application/vnd.api+json
↪ paths./v1/catalog/categories.get.responses[200].content.
↪ application/json
Spectral 报告在 API 定义文件第 41 行出现了问题。JSON:API 规范要求所有响应都必须使用 Content-Type: application/vnd.api+json
头部,但 GET /v1/catalog/categories
操作的响应定义却用了 application/json
。
文件第 40 至 43 行代码片段如下:
less
content:
application/json: # 这里第 40 行定义了 application/json,而非 application/vnd.api+json
schema:
$ref: '#/components/schemas/CategoriesResponse'
回到 Spectral 的问题报告,可以看到输出包含:行号和列号(41:30)、问题严重程度(error)、违反的规则名(response-content-type)、规则对应的提示信息(所有 JSON:API 响应体必须返回......),以及 API 定义文件中有问题的具体路径(paths./v1/catalog/categories.get.responses[200].content.application/json)。
3.5 在 VS Code 中运行 Spectral 代码规范检查
如果你按照附录 B 的说明操作,应该已经在电脑上安装了 VS Code,并安装启用了 Spectral 插件。你可以配置 Spectral 插件,使其使用你的规则集文件来校验 API 定义文件。具体操作是:打开 VS Code 设置,搜索 Spectral 插件,在 "Spectral:Ruleset" 配置项中填写 json-api.ruleset.yaml
。
然后返回到你的 product-catalog-v2.oas.yaml
API 定义文件。从第 41 行开始,VS Code 会高亮显示存在问题的代码行。将鼠标悬停在问题行上,会显示该问题的详细信息。你也可以在编辑器底部的 "Problem"(问题)标签页中查看错误消息,如图 3.2 所示。

你可以看到,通过将 Spectral 集成到你的 API 设计编辑器中,在设计阶段编辑 API 定义文件时,你能够快速获得关于 API 规范符合情况的反馈。在本书中,我使用 VS Code 作为 OpenAPI 文件的编辑器,并配合 42Crunch OpenAPI(Swagger)插件一起使用,但市面上还有其他流行的 OpenAPI 编辑器。很多编辑器是专为团队工作流和 API 评审设计的 API 设计与协作工具,例如 Stoplight、Postman、Apicurio、SwaggerHub 和 Insomnia。这些工具中有些支持可视化界面来编辑 OpenAPI 定义文件、添加注释、进行 API 代码规范检查以及模拟请求。
3.6 创建一个针对 info.description 的 Spectral 规则
到目前为止,你运行的都是已有规则集中的 Spectral 规则。那么如果你想为你们组织的风格指南写一个自定义的 Spectral 规则,该怎么做呢?比如,你被要求编写一个规则,要求 OpenAPI 定义文件的 info 对象中必须包含 description 字段。举例来说,下面这个缺少 $.info.description
字段的最简 OpenAPI 定义片段应该被判定为不合规:
yaml
openapi: 3.0.3
info:
title: Acme Product Catalog API
version: 0.0.1
paths: {}
你该如何编写这个规则呢?首先,你需要了解 Spectral 规则的基本结构。Spectral 的规则集文件可以是 JSON、YAML 或 JavaScript 格式,但本章我们将用 YAML 来定义规则集。Spectral 规则集文件的顶层有一个 rules
字段,里面是一个包含多个规则的数组。每条规则都有一个名称和若干属性。针对你想创建的这个简单规则,可以用四个属性:message
、given
、then
和 severity
。
message
属性的值是当规则被违反时显示的提示信息,它应当给读者一个有用的、清晰的问题说明。对于你的例子,可以写成:"API 定义文件必须包含 description 字段"。given
属性是一个 JSONPath 表达式或表达式集合,用来指定规则应用于文档的哪些部分。JSONPath 表达式可以返回零个或多个元素,如果返回零个,规则不会被执行。这里given
可以设置为$.info
,表示规则应用于整个 info 对象,这样你就能在then
中指定对这个对象检查是否包含 description 字段。
注释 :JSONPath 是一种用于查询 JSON 文档的语言,类似于 XML 的 XPath。JSONPath 表达式以
$
开头,表示文档的根节点,后面跟用点号(.
)或方括号([]
)分隔的子元素路径。例如,前面 OpenAPI 片段的 JSONPath$.info.version
会返回值0.0.1
。更多关于 JSONPath 的内容请参考 github.com/json-path/J... 。
-
then
属性是针对given
返回的对象进行规则判断的地方。then
主要有两个关键字:function
和field
。field
是可选的,用于指定具体检查对象中的某个字段。在你的规则中,应设为description
,表示你要确保 info 对象中存在这个字段。function
用来指定 Spectral 要执行的检查函数。只有当检查发现问题时,函数才会报告违规。Spectral 自带一些内置函数,也支持你用 JavaScript 或 TypeScript 自定义函数。这里的function
应该设为truthy
,这是 Spectral 的核心函数之一,用于检查某个值是否为真(即不为 false、空字符串、0、null 或 undefined)。如果info.description
是 null,函数就会触发,报告违规。
-
severity
属性指定规则的严重级别,用于分组和筛选违规结果。在运行 Spectral CLI 时,可以用--fail-severity
参数指定达到某个严重级别(或更高)的违规将导致命令返回失败退出码。Spectral 从高到低有五个严重级别:error
、warn
、info
、hint
和off
。
图 3.3 展示了规则集文件中这些属性的结构示意。

图 3.4 展示了一个简化的流程图,说明了四个规则元素(message、given、then、severity)。

现在你已经了解了 Spectral 规则的基本结构,可以创建一个规则来检查 $.info.description
是否已定义。找到项目中的 simple.ruleset.yaml
文件,并用 VS Code 或你喜欢的文本编辑器打开。输入清单 3.1 中的代码,然后保存文件。
清单 3.1 检查 info.description 是否存在的 Spectral 规则
yaml
rules:
info-description-is-required: #1
message: "API definition files must
↪ have info.description defined" #2
severity: error #3
given: $.info #4
then:
field: description #5
function: truthy #6
注释说明:
- #1 规则名称
- #2 规则违背时显示的提示信息
- #3 规则的严重级别
- #4 JSONPath 表达式,指定要检查的文档元素
- #5 指定要检查的元素中的字段
- #6 Spectral 的 truthy 函数,当
$.info.description
为 null 时触发
尝试在一个缺少 info.description
元素的 API 定义文件上测试新规则。在命令行中执行:
css
$ spectral lint minimal.oas.yaml --ruleset simple.ruleset.yaml
你应该会看到如下输出:
vbnet
2:6 error info-description-is-required API definition files must have
↪ info.description defined info
✖ 1 problem (1 error, 0 warnings, 0 infos, 0 hints)
这样你就成功创建并执行了第一个 Spectral 规则。你用了 Spectral 的 truthy 函数,它会检查 JSONPath 表达式返回的值是否为真。Spectral 还自带其他核心函数,比如 length
(用于计算字符串、数组和对象属性长度,并检测是否满足最大或最小值)、casing
(检测文本是否符合指定的大小写格式,如 CamelCase 或 kebab-case)和 pattern
(用于正则表达式匹配)。Spectral 核心函数列表可以参考:mng.bz/gvml。
在刚才的场景中,truthy 函数没有额外的输入或 functionOptions
。functionOptions
是传给 Spectral 函数的附加输入值。接下来,我们将看到需要用到 functionOptions
的场景。
3.7 为路径版本号创建 Spectral 规则
假设你需要实现一个 API 风格指南规则,要求所有 URI 路径必须以版本号开头。也就是说,你要选中 API 定义文件中的所有 URI 路径,并检查它们是否以 /v{数字}/
模式开头。假设你要针对 product-catalog-v3.oas.yaml
文件运行该规则。
为了创建这个规则,given
属性应是一个 JSONPath 表达式,用来选择 API 定义文件中的所有 URI 路径,但基础 JSONPath 无法实现这个功能。Spectral 支持扩展的 JSONPath 表达式,叫做 JSONPath Plus。你需要的 JSONPath Plus 表达式是:
css
$.paths[*]~
它能获取文档中的所有路径。更多 JSONPath Plus 表达式内容见:github.com/JSONPath-Pl...
测试 JSONPath Plus 表达式
你可以先把 product-catalog-v3.oas.yaml
文件转换成 JSON,再用 JSONPath Plus 表达式测试是否能正确获取所有路径。转换命令(需要你已经安装了第 B 章提到的 yq 工具):
ini
$ yq eval -o=json product-catalog-v3.oas.yaml > product-catalog-v3.oas.json
测试 JSONPath Plus 表达式的方式有两种:
- 使用在线 JSONPath Plus 测试工具(如 JSON Query <www.jsonquerytool.com>),将
product-catalog-v3.oas.json
内容复制进去,选择 JSON Path Plus 转换,输入查询表达式$.paths[*]~
。 - 使用命令行工具 jpp(同样在附录 B 中有安装说明):
ruby
$ jpp "$.paths[*]~" product-catalog-v3.oas.json
这会输出匹配的路径数组。
接下来,你要指定 then
属性,调用 Spectral 的 pattern
函数执行正则匹配,但需要给它传递正则表达式,使用 functionOptions
关键字:
sql
then:
function: pattern
functionOptions:
match: '^/v\d+/.*$'
functionOptions
中有两个关键字:match
(匹配)和 notMatch
(不匹配)。match
指定要匹配的正则表达式,而 notMatch
则要求被检测元素不匹配该表达式。
你还可以为规则添加 description
属性,简短描述该规则,帮助阅读规则文件时更好理解。
此外,message
属性可以使用占位符,在运行时动态替换,比如:
{{description}}
:规则描述{{property}}
:触发违规的 API 元素名{{path}}
:违规元素的完整路径
现在,在 simple.ruleset.yaml
文件中添加以下规则:
清单 3.2 添加路径版本号检查规则
yaml
rules:
info-description-is-required:
message: "API definition files must have info.description defined"
severity: error
given: $.info
then:
field: description
function: truthy
uri-path-must-include-version: #1
description: The URI path must include the version #2
message: '{{description}}; {{property}} should start with /v{digit}/' #3
severity: error
given: '$.paths[*]~' #4
then:
function: pattern
functionOptions: #5
match: '^/v\d+/.*$'
注释说明:
- #1 新增规则名称
- #2 规则简短描述
- #3 消息中使用了占位符,动态输出规则描述和出错字段
- #4 JSONPath Plus 表达式,选取 API 中所有 URI 路径
- #5 通过
functionOptions
给pattern
函数传入正则表达式
执行新规则,命令行输入:
css
$ spectral lint product-catalog-v3.oas.yaml --ruleset simple.ruleset.yaml
Spectral 会报出如下问题:
bash
The URI path must include the version: /catalog/categories should start with /v{digit}/.
这样,你就完成了 simple.ruleset.yaml
文件中的第二条 lint 规则。你可以在该规则集中继续添加更多规则,或者导入其它规则集。
接下来,我们将介绍如何在 Spectral 中导入或扩展规则集。
3.8 扩展规则集
Spectral 允许你通过 extends
属性在规则文件中导入其他地方定义的规则集。被导入的规则集可以是 Spectral 内置的规则集、本地文件系统中的规则集、HTTP 服务器上的规则集,或者定义在 npm 包中的规则集。要导入规则集,只需使用 extends
属性。你可以尝试在项目中找到 .spectral.yaml
文件(目前是空的),输入下面的示例,它导入了 Spectral 内置的 OAS(OpenAPI Specification)规则集。Spectral 的 oas 规则集包含了一些通用规则,适用于 OpenAPI 2.x 和 3.x 定义,适合用来检查 OpenAPI 定义文件的有效性。
清单 3.3 继承 Spectral oas 规则集的基础规则文件
php
extends: ["spectral:oas"] #1
注释
- #1
extends
属性接收一个规则集数组,这里只导入了 Spectral 内置的 oas 规则集。
在 minimal.oas.yaml
API 定义文件(只包含满足 oas 最基本要求的元素)上测试这个规则集:
ruby
$ spectral lint minimal.oas.yaml
注意:如果你没有指定规则文件,Spectral 会在当前工作目录下查找 .spectral.yaml
、.spectral.yml
或 .spectral.json
文件,并使用它作为规则集。它应该会找到你的 .spectral.yaml
并输出类似如下内容:
php
1:1 warning oas3-api-servers OpenAPI "servers" must be present
↪ and non-empty array.
2:6 warning info-contact Info object must have "contact"
↪ object. info
2:6 warning info-description Info "description" must be present and
↪ non-empty string. Info
Spectral 报告了三个警告级别问题,没有错误级别问题,这表明你的 API 定义文件是有效的。要查看 Spectral oas 规则集里用于校验的完整规则列表,请参考官方文档:mng.bz/ppZG。
当你导入规则集时,可以修改其中规则的严重性。例如,你想关闭导入的 oas 规则集中的 info-description
规则,可以修改 .spectral.yaml
文件如下:
清单 3.4 关闭导入的 info-description 规则
vbnet
extends: ["spectral:oas"]
rules:
info-description: off #1
注释
- #1 将导入规则的严重级别设置为
off
,表示禁用该规则。
重新运行:
ruby
$ spectral lint minimal.oas.yaml
此时输出应该只剩两个问题,不会再显示 info-description
警告。
公开的 Spectral 规则集
你可能想知道有没有公开的 Spectral 规则集可以直接复用,以下是三个参考:
- Postman Governance Rules
Postman 工作区,内含 Spectral lint 规则作为 Postman 集合,便于你 fork 和测试:mng.bz/OZwn - APIs You Won't Hate API 风格指南
Phil Sturgeon 维护的一组规则集,实现其《Build APIs You Won't Hate》书中推荐的 API 规范:github.com/apisyouwont... - Azure API 风格指南
微软 Azure 官方 GitHub 仓库,包含用于验证 Azure 服务 OpenAPI 定义的规则集:github.com/Azure/azure...
假设你想扩展一个定义在 HTTP 服务器上的规则集,只需在 extends
属性中添加规则集的 URI,如下示例。注意,被扩展的规则集支持所有 Spectral 规则格式:YAML、JSON、JavaScript、TypeScript。
清单 3.5 扩展远程规则集
bash
extends:
- spectral:oas
- https://unpkg.com/@apisyouwonthate/style-guide@1.3.2/dist/ruleset.js #1
rules:
info-description: off
注释
- #1 将远程规则集 URI 添加为导入的规则集
再次运行对 minimal.oas.yaml
的 Spectral lint,应该会看到从远程规则集导入的两个额外问题(如 api-health
和 api-home
)。
3.9 覆写 API Lint 规则
当你有一个全面的 lint 规则集用于一个全新 API 项目时,规则可以指导设计阶段,允许你在还没写代码时修改设计以符合规范。但如果你已经有了生产环境运行的 API 项目,它并不完全符合新的 lint 规则,该怎么办?
你可以采用三种方法:
- 直接采用规则集,修改项目设计和代码,使之符合规则。这个"大爆炸式"方法工作量大,可能涉及破坏性改动,对 API 用户影响较大(破坏性改动将在第4章讲解)。
- 每个项目使用不同规则集(多规则集方法) ,为现有项目创建只包含通过规则的新规则集,保证对新扩展的 API 起码满足最小规则。但这会导致多个规则集的重复和不一致。
- 同一规则集,逐步启用(渐进一致性方法) ,只启用当前通过的规则,针对失败的规则对特定 API 对象禁用或降低严重度。这样新改动会自动符合当前标准,有时间时再对禁用规则逐步修复,提升一致性。部分规则可能永远禁用。
这种渐进式一致性依赖于能针对特定 OpenAPI 文档和对象,禁用或修改某些规则的严重级别。Spectral 使用 overrides
功能实现此目的。overrides
允许你为不同项目或文档定制同一规则集。它通过文件路径通配符和 JSON 指针(JSON Pointer,详见附录 C)指定需要覆写的文档和对象,并指定要调整的规则名和严重级别,如图 3.5 所示。

让我们来看一个实际例子。假设有一个 chapter3/overrides/openapi.yaml
文件,内容部分如下。注意,GET /users/{id}
操作定义了 operationId
,但 PUT /users/{id}
和 DELETE /users/{id}
操作没有定义:
yaml
openapi: 3.0.0
info:
title: User API
version: 1.0.0
servers:
- url: http://localhost:8080
paths:
/users/{id}:
get:
summary: Get a user by id
operationId: getUserById #1
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: OK
put:
summary: Update a user by id #2
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: OK
delete:
summary: Delete a user by id
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'204':
description: No Content
#1 定义了 operationId
#2 没有定义 operationId
假设你有一个 operation-must-have-operationId
规则,但想在整个文件中禁用它,你的规则集文件可以像 chapter3/overrides/ruleset1.yaml
那样写:
yaml
overrides:
- files:
- openapi.yaml #1
rules:
operation-must-have-operationId: off #2
rules:
operation-must-have-operationId:
message: 'Each operation must have an operationId'
severity: error
given: '$.paths[*][get,put,post,delete,options,head,patch,trace]'
then:
field: operationId
function: truthy
#1 禁用该规则在整个文件中生效
#2 将该规则的严重性设置为 off,即禁用
进入 chapter3/overrides
目录,运行命令:
css
spectral lint openapi.yaml --ruleset ruleset1.yaml
不会出现任何 lint 报错,输出为:
csharp
No results with a severity of 'error' found!
接下来,关闭 PUT /users/{id}
操作的规则,但把该规则的严重性改为警告(warn),如 chapter3/overrides/ruleset2.yaml
所示。注意,指向 API 操作的 JSON 指针必须进行百分号编码(因此是 /paths/~1users~1%7Bid%7D/put
,而不是 /paths/~1users~1{id}/put
):
yaml
overrides:
- files:
- openapi.yaml#/paths/~1users~1%7Bid%7D/put #1
rules:
operation-must-have-operationId: off
- files:
- openapi.yaml#/paths/~1users~1%7Bid%7D/delete
rules:
operation-must-have-operationId: warn #2
rules:
operation-must-have-operationId:
message: 'Each operation must have an operationId'
severity: error
given: '$.paths[*][get,put,post,delete,options,head,patch,trace]'
then:
field: operationId
function: truthy
#1 百分号编码的 JSON 指针
#2 规则严重级别从 error 覆盖为 warning
运行 lint,结果只显示 delete
操作的警告级别违规,而 put
操作虽然也有同样问题,但被禁用,不会报错,如下:
sql
$ spectral lint openapi.yaml --ruleset ruleset2.yaml
36:12 warning operation-must-have-operationId Each operation must have an operationId paths./users/{id}.delete
至此,你已经了解如何在 Spectral 中覆写 API 的 lint 规则。对于已有生产环境的 API,引入 lint 规则时,建议使用渐进一致性方法,通过 overrides 逐步启用规则。但请谨慎使用此功能,因为尽可能的目标是让 API 设计完全符合风格指南。
另外,如你所见,编写 lint 规则时必须严格遵循语法规范。Spectral 需要你提供 JSONPath 表达式,有时需要借助 JSONPath 表达式评估器来调试确保正确。
如果能有一个 AI 助手帮你快速创建 lint 规则,并帮你生成对应的测试来快速验证规则是否有效,那该多好!接下来,我会介绍如何做到这一点,但首先让我带你认识一下 AI 助手。
3.10 引入 AI 编程助手
AI 助手是一种利用人工智能,根据用户的命令或查询执行任务的软件代理。AI 助手能够理解人类语言,这得益于自然语言处理(NLP)技术,使用户可以通过文本提示与它们交互。其中一种特定类型的 AI 助手是 AI 编程助手,专为软件开发人员设计。这些助手能为整行代码或整个函数提供建议和补全。常见的 AI 编程助手有 GitHub Copilot、Tabnine、Amazon CodeWhisperer、Sourcegraph Cody 和 JetBrains AI。
编程助手帮助开发者分析代码、生成代码,并提供个性化且相关的代码改进建议,包括修复 bug。它们通过向大型语言模型(LLM)发送指令和代码上下文实现这一点。LLM 是一种基于庞大数据集训练的 AI 模型,能够生成类人响应。LLM 使用深度学习技术,如 Transformer(一种专门理解语言上下文关系和复杂语言任务的神经网络),来理解语言的上下文、语法和语义,从而完成内容生成、翻译和对话等任务。LLM 会随着新数据和反馈不断学习和改进。
GitHub Copilot 是由 GitHub 设计并由 OpenAI 研发的 LLM 支持的 AI 助手。它能为多种编程语言和软件框架提供建议,尤其擅长 Python、JavaScript、TypeScript、Go、Ruby、C++ 和 C#。通过自动化常规编码任务,Copilot 相当于一名初级的编程搭档,提高了抽象层次,让开发者专注于更高层次的问题,减少对模板代码和语法细节的关注。简而言之,Copilot 帮助开发者提升生产力。
编程助手旨在提高开发效率,但不能替代对编程语言和工具基础概念的理解。开发者仍需测试和审查生成的代码。原因之一是 LLM 可能出现"幻觉"现象,即生成错误、无意义或误导性信息并错误地呈现为准确内容。此外,LLM 不是搜索引擎,通常不会引用其回答依据。
在使用 LLM 时,还需考虑数据隐私、许可和版权等法律伦理问题。LLM 的训练数据来自代码库,可能存在偏见。如前所述,生成的代码可能在语义或语法上不正确,甚至存在安全漏洞风险。
提示:想了解 LLM 的通用(且有趣)介绍,可以参见 David Clinton 著的《The Complete Obsolete Guide to Generative AI》(Manning, 2023;mng.bz/5l2D)。
接下来,我将介绍如何使用 Copilot 作为编程助手生成 API lint 规则。为此,你需要向 Copilot 提供提示,具体如下。
3.10.1 使用 Copilot 创建 lint 规则的提示语
提示工程(Prompt engineering)指的是精心设计良好的提示语------包括指令、上下文和示例,帮助 LLM 生成更有用的响应。GitHub Copilot 插件会分析你的代码上下文帮助提示工程,但你也可以通过提供提示和示例来辅助它。
Copilot 会在你输入时提供内联建议,按 Tab 键即可接受。Copilot 聊天扩展提供了一个内联聊天界面,方便你边写代码边与 Copilot 交流。下面介绍如何用它来创建一个 lint 规则。
在第 3.6 节中,你写了一个验证 OpenAPI 文件中 $.info.description
字段存在的 lint 规则。现在让 GitHub Copilot 帮你创建这条规则。首先,创建 chapter3/copilot/rules.yaml
文件。打开后,在 Mac 上按 ⌘--I,Windows 上按 Ctrl--I,调出 Copilot 内联聊天,然后输入以下提示:
csharp
Write a Spectral linting rule for an OpenAPI file that checks that the info.description field is present
由于 LLM 是非确定性的,生成的代码每次可能不同。我的结果是:
yaml
rules:
openapi-info-description-present:
description: "Check if the info.description field is present"
message: "The info.description field is missing"
given: "$.info"
then:
field: "description"
function: "truthy"
点击按钮接受建议。接着,写一个测试来验证规则是否生效。创建 chapter3/copilot/openapi.yaml
文件,打开后同样调出 Copilot 内联聊天,输入提示:
javascript
Create an OpenAPI document to test the rule. The document should make the rule fail.
Copilot 生成了以下内容:
yaml
openapi: 3.0.0
info:
title: My API #1
version: 1.0.0
paths:
/path/to/endpoint:
get:
summary: Endpoint Summary
description: Endpoint Description
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
#1 未包含 info.description 属性
切换到 chapter3/copilot
目录,执行以下命令对测试文档应用 lint 规则,验证规则是否有效:
css
spectral lint openapi.yaml --ruleset rules.yaml
你应该会看到规则校验违规,说明规则有效。
以下是一些在 VS Code 中为 Copilot 提供好提示的小技巧:
- Copilot 会查看你当前编辑的文件以及其他打开的相关文件。保持相关文件(如 OpenAPI 文档和 Spectral 规则集)打开,有助于 Copilot 生成更合适的建议。
- 在文件顶部写一个简要的注释介绍文件内容,给 Copilot 提供整体上下文。
- 提供类似的示例代码,如相关 lint 规则,帮助 Copilot 理解你的需求。示例用完后可删除。
- 编写 JavaScript Spectral 规则时,在文件顶部引入相关依赖和库,让 Copilot 知道用到哪些工具。使用有意义的函数名和注释也有助于 Copilot 理解并生成代码。注释中可以说明函数的输入输出。
- 合理安排代码编写顺序,我通常先写 lint 规则,再让 Copilot 创建测试用的 OpenAPI 文档。
- Spectral lint 规则中 JSONPath、JSON 指针和正则表达式编写较为复杂,幸运的是 Copilot 可以帮助生成这些表达式。你也可以让 Copilot 生成测试代码,比如用
console.log()
打印结果验证表达式正确性。
提示:关于使用 Copilot 处理正则表达式的示例视频,请观看 www.youtube.com/watch?v=dTA... 的入门视频见 www.youtube.com/watch?v=Fi3...
3.11 使用大型语言模型(LLM)进行自动化 lint 检查
假设你想创建一条 lint 规则,要求所有请求和响应对象中,属性名暗示是货币金额的字段,其值必须是数字格式;也就是说,不允许货币金额用字符串表示。例如,这条规则应确保名为 cost_price
的属性值是数字格式。你当然可以写一条 Spectral lint 规则,检查任何名为 cost_price
的属性值是否为数字,但如果属性名是 selling_price
或仅仅是 price
呢?还有 amount
和 vat_amount
呢?货币金额暗示的属性名太多,全部定义在一条 Spectral 规则里显然不现实。
再比如,你想自动检查每个属性的描述(description)是否有意义。比如,属性 vat_amount
不能只写描述为"the vat_amount",而应该写成更有意义的描述,比如"Amount of value added tax (VAT) added to an item's price. This depends on the configured VAT rate." 那么,如何写一条 Spectral 规则来确保每个属性的描述都是有意义的呢?这不仅仅是检查描述长度或者用正则表达式可以解决的,用 Spectral 单独做这事也很困难。
还有更多类似的通用 lint 规则你可能想执行:
- URI 路径必须使用名词表示资源。
- 如果 GET 操作的查询参数中出现敏感信息或个人身份信息(例如
date_of_birth
或first_name
),则要求该操作使用 POST 方法。
对于这类情况,写 Spectral 规则往往不可行或不切实际。通常团队会在 API 评审阶段(第 5 章讨论 API 设计评审)来发现这类问题。但有一种自动化方法------使用大型语言模型(LLM)。LLM 可以根据你提供的规则对代码进行 lint 检查,并以你指定的格式给出违规报告。使用 LLM 进行自动化 lint,有成本收益优势------比起高级工程师耗费时间参与 API 评审,使用 LLM 自动化检查代码变更要便宜得多。而且,使用自然语言来定义 lint 规则,还节省了学习 Spectral 或其他 lint 工具语法的时间,团队中不熟悉这些语法的成员也能写规则。
3.11.1 使用 Anthropic Claude 进行 lint 检查
我们来看一个利用 LLM 进行 OpenAPI lint 检查的例子。假设你想用前面提到的场景:所有暗示货币金额的属性名,其值必须是数字格式。你想对 chapter3/llm-linting/openapi.yaml
这个只有一个操作(GET /v1/products)的 OpenAPI 文件做 lint。该操作返回的响应有四个属性:id
、cost_price
、selling_price
和 vat_amount
。现在用 Anthropic Claude 试试。(如果你按照附录 B 的指导操作,已经配置好了 Anthropic Claude 账号。)
登录 Anthropic Claude(claude.ai),用聊天界面上传,-394fp1d0x9af06ckjai48gek0d/) chapter3/llm-linting/openapi.yaml
文件。然后给它输入以下提示:
css
I have attached an OpenAPI document. List all response properties whose names imply that they are a monetary amount.
我执行时,Claude 给出的回答是:
基于你提供的 OpenAPI 文档,响应中属性名暗示为货币金额的属性有:
- cost_price
- selling_price
- vat_amount
原因如下:
cost_price
--- 名称中的 "cost" 暗示这是商品的成本价,是货币金额。selling_price
--- 名称中的 "price" 明确表示这是商品售价,是货币金额。vat_amount
--- "VAT" 指增值税,是对商品征收的货币金额。
总结,这三个属性名都明确表示它们存储的是货币/货币单位的值。
接着,给 Claude 输入以下提示,进行 lint 检查:
python
Lint the OpenAPI document based on the following rule:
- If a property's name implies it is a monetary amount, it must use number format. Show me the linting rule violations. An example of a violation can be: "The property 'cost_price' implies a monetary amount but is defined with 'type: string'. It should be defined with 'type: number' to accurately represent a monetary value."
Claude 返回的回答是:
基于"暗示货币金额的属性必须使用数字格式"的 lint 规则,以下是 OpenAPI 文档中的违规项:
- 属性
cost_price
暗示货币金额,但定义为'type: string'
,应改为'type: number'
,以准确表示货币值。 - 属性
selling_price
暗示货币金额,但定义为'type: string'
,应改为'type: number'
,以准确表示货币值。 - 属性
vat_amount
暗示货币金额,但定义为'type: string'
,应改为'type: number'
,以准确表示货币值。
总结:cost_price
、selling_price
和 vat_amount
这三个属性违反了"货币金额属性应使用数字类型"的规则。将它们定义为数字类型会使 API 更准确。
你并没有告诉 Claude 具体要检查哪些属性名,它还是成功找出了你预期的这几个属性,并且用了你指定的格式给出了违规提示。
但通过聊天界面操作对多数开发者来说并不方便,他们大多时间还是在文本编辑器或 IDE 里工作。你可以用 Optic API 工具(www.useoptic.com)将-jw9g/) LLM 集成到你的 CI/CD 流水线中,实现自动化 lint 工具的效果。
3.11.2 使用 Optic 的 LintGPT 进行 lint 检查
Optic 是一个自动化的 API 管理工具,提供 API 发现、破坏性变更检查以及 OpenAPI 差异生成等功能。Optic 支持基于自然语言的 AI lint 规则,称为 LintGPT。Optic 是专有工具,但提供免费套餐。不过截至本文撰写时,使用 LintGPT 规则需要付费的企业版套餐。这里简单介绍一下它的工作原理。
首先,你需要在项目根目录下的配置文件 optic.yaml
中指定 LintGPT 规则。下面是两个 LintGPT 规则的示例:
yaml
ruleset:
- lintgpt:
v3_standards:
rules:
- "If a property's name implies it is a monetary amount, it must use number format." #1
- "GET responses with a top level data property of type array should have some form of pagination." #2
#1 使用"must"表示错误级别(error)严重性。
#2 使用"should"表示警告级别(warning)严重性。
当 Optic 执行 LintGPT 规则时,可能会返回如下错误:
sql
[Warn] The response for the GET request contains a top-level 'data' property of type array but does not include any form of pagination. Pagination is important for performance and usability when dealing with potentially large datasets.
[Error] The properties 'cost_price', 'selling_price', and 'vat_amount' imply monetary amounts but are defined with 'type: string'. They should be defined with 'type: number' to accurately represent monetary values.
Optic 提供了一个用户界面,能够清晰地显示出 API 定义文件中哪些部分存在 lint 违规,如图 3.6 所示。

总结一下。你已经看到,LLM 可以用来定义 lint 规则,这些规则也可以轻松地创建在传统的 lint 工具中,比如 Spectral。LLM 使得用自然语言定义 lint 规则成为可能,从而让团队中更多人(不仅仅是懂某个 lint 工具语法的人)都能编写 lint 规则。不过,在使用 LLM 时,你需要测试其输出,确保它能准确地发现问题,然后再推广使用。
总结
- API 风格指南是包含组织 API 设计规范和指南的参考文档。
- API lint 是一种自动化检查 API 定义是否符合风格指南的方法,是 APIOps 设计阶段的重要环节。
- 你可以使用 Spectral 作为 API lint 工具,执行基于风格指南的规则集。Spectral 可通过命令行运行,也集成了多种编辑器和 API 设计工具。
- 编写 Spectral 规则时,需要定义定位 API 定义文档中要检查元素的 JSONPath 表达式,并指定 Spectral 函数来完成检查。
- 通过 extends 属性,可以导入一个或多个本地或远程定义的 Spectral 规则集。
- 给已有 OpenAPI 文档添加新规则时,可以采用渐进一致性的方法,对文档特定部分禁用某些规则。
- AI 编码助手可以帮助你创建 API lint 规则,也可以让 LLM 根据你提供的规则对 OpenAPI 文档进行 lint 检查。