使用 API-First 方法构建 RestfulAPI(一)
原文标题:Designing Restful APIs using an API-First Approach
原文链接:https://dev.to/ntakashi/designing-restful-apis-using-an-api-first-approach-23e2
作者:Nicolas Takashi
我使用 Restful API 已经有一段时间了,今天我将向大家展示如何使用 API-First 方法和使用 OpenApi 规范来构建 API。
首先,如果你不知道什么是 API-First ,可以先通过看看这篇文章《API 优先、API 设计优先和代码优先》,让自己对 API-First 有初步的认识。
📋 基础准备
在开始动手之前,让我们做好准备并了解将要开发的用例。
工具
如果你希望复现本文使用的示例,你需要准备好这些工具:
- NodeJS
- OpenAPI 规范
- 文本编辑器(我使用的是 VSCode)
- 命令行
用例
为了易于理解,我们将使用 Todo List App,这是软件开发社区里一个非常普遍的方法。
免责声明
为了让大家更容易阅读 gist 文件,我将会省略一些不重要的信息。
🙌 开始编写
现在你已经拥有了全部所需的环境,让我们正式开始吧!
第一步是为 OpenAPI 规范创建入口点文件并向该文件添加一些内容。
openapi: 3.0.0
info:
title: Todo App API
description: Todo App API.
version: v1
contact:
email: 'nicolas.tcs@hotmail.com'
name: 'Nicolas Takashi'
paths:
#... there more content here.
上面的文件是 OpenAPI 文档的一个非常简单的示例,只是为了向你介绍这个概念。OpenAPI 的编写结构非常清晰易懂,但如果你还不知道它是如何工作的,我建议你可以先阅读官方文档。
编写 Path 对象
Path 对象包含每个端点及其操作的相对路径,让我们添加一个名为 tasks.yaml
的文件,内容如下:
get:
operationId: get-tasks
summary: Returns a paged list of tasks.
description: Returns a paged list of active tasks
tags:
- Tasks
parameters:
- $ref: '../../parameters/page-parameters.yaml#/components/parameters/page'
- $ref: '../../parameters/page-parameters.yaml#/components/parameters/pageSize'
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '../../schemas/pagedTask.yaml#/components/schemas/PagedTask'
post:
operationId: create-task
summary: Create a task
description: Create a new task
tags:
- Tasks
requestBody:
required: true
content:
application/json:
schema:
$ref: '../../schemas/task.yaml#/components/schemas/Task'
responses:
'201':
description: Created
headers:
Location:
description: 'Location of the created task /tasks/{id}'
schema:
type: string
你可以看到上面的代码中,添加了两个操作,这将使 Todo List API 能够创建任务并获取任务的分页列表。
现在我们可以添加操作使 API 能够删除任务、查找特定任务和更新任务,为此我们必须创建一个名为的新文件tasks-with-id.yaml
并添加以下内容:
get:
operationId: get-task
summary: Get task by id
description: Return a task
tags:
- Tasks
parameters:
- in: path
name: id
description: Task id
required: true
schema:
type: string
format: uuid
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '../../schemas/task.yaml#/components/schemas/Task'
put:
operationId: update-task
summary: Update a task
description: Update a task
tags:
- Tasks
parameters:
- in: path
name: id
description: Task id
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
$ref: '../../schemas/task.yaml#/components/schemas/Task'
responses:
'202':
description: No Content
delete:
operationId: delete-task
summary: Delete a task
description: Delete a task
tags:
- Tasks
parameters:
- in: path
name: id
description: Task id
required: true
schema:
type: string
format: uuid
responses:
'202':
description: No content
使用引用
在软件开发中经常需要共享代码,而在我们设计 API 时也会发生同样的情况。遵循 OpenAPI 规范使我们能够跨 OpenAPI 文档并重新利用公共对象。
使用$ref
符号,你可以添加对 API 的本地或远程引用(References),比如你在上面的示例中所见的代码。如果你想更好地了解如何使用 References 以及可以在什么地方使用它,请查看官方文档。
编写 Schema 对象
我们正在使用一个名为 Task 的架构组件,该架构在整个 API 中用于表示资源任务,该架构示例如下:
components:
schemas:
Task:
type: object
required:
- name
- id
properties:
id:
description: Task unique identifier
type: string
format: uuid
readOnly: true
name:
description: Task name
type: string
example: Todo Service
通过上面的代码,我们可以在所有 API 中重新利用 Task 模式并避免代码重复。
编写 Parameter 对象
就像 Schemas 一样,我们可以对参数(Parameters)做同样的事情,如果有跨 API 共享的参数,我们可以引用本地或远程文件,这里我们将创建两个参数来表示有关页面及其大小的信息:
components:
parameters:
page:
in: query
description: Desired page
name: page
schema:
type: integer
format: int32
default: 1
minimum: 1
pageSize:
in: query
description: Desired quantity of itens per page
name: pageSize
schema:
type: integer
format: int32
default: 10
minimum: 10
maximum: 100
最终的 API 入口点
看完这些文件的单独示例之后,让我们看看把这些示例连起来用的效果是怎样的:
openapi: 3.0.0
info:
title: Todo App API
description: Todo App API.
version: v1
contact:
email: 'nicolas.tcs@hotmail.com'
name: 'Nicolas Takashi'
tags:
- name: Tasks
paths:
'/tasks':
$ref: './components/paths/tasks/task.yaml'
'/tasks/{id}':
$ref: './components/paths/tasks/task-by-id.yaml'
components:
schemas:
Task:
$ref: 'components/schemas/task.yaml#/components/schemas/Task'
PagedTask:
$ref: 'components/schemas/pagedTask.yaml#/components/schemas/PagedTask'
我们设计了一个 Todo List API,接下来优化一下 API 设计,如果你像我一样使用 Visual Studio ,你可以安装一个名为 Swagger View 的扩展,这样你就可以渲染出像下图一样的效果:
swagger 视图看起来还不错,每个客户都可以通过 Swagger-UI 探索你的 API。
📦 打包 OpenAPI 文档
在开始使用 lint 和 OpenAPI 文档交互以及为我们的客户创建模拟服务器和 SDK 之前,我们必现打包 API 才能开启机器读取和交互。
为了在设计过程中帮助你避免重复和增强代码重用,你可以将 OpenAPI 文档拆分为多个文件并在之后引用这些文件。但这不是有效的 OpenAPI 文档,现在,我们将了解如何使用 swagger-cli 将许多文档打包到一个文档中。 安装swagger-cli
非常简单,只需要运行下面的命令就可以了:
// using npm
npm install swagger-cli
// using yarn
yarn add swagger-cli
之后,你只需要运行下面的 bundle 命令:
swagger-cli bundle api.yaml -o ./bin/api.yaml -t yaml
完成后,你已经将 API 打包到一个文件中了,现在你可以检查该文件以确保遵循了 Restful 规范。
📐 Restful 规范
当我们设计 API 时,有一件非常重要的事情就是定义规范。
软件社区提供了很多设计 RestfulAPI 的规范,但在很多情况下,这些规范不符合你的业务需求,你需要创建自己的规范。
比定义规范更重要的是确保你的 API 遵循这些规范,例如,确保每个请求都有 「200 响应」。
但是我们知道,如果不使用工具来完成这项工作是很难的,所以让我向你介绍这款好工具,并在这项工作中为你提供支持。
使用 spectral 验证 OpenAPI 文档
你很难通过手动的方式去检查你的 API 是否按照贵公司建议的规范进行设计。因此,我们可以使用像 Spectral 这样的工具来帮助我们创建一个自动化流程来完成这项工作。Spectral 是一个开源项目,它的工作方式类似于 OpenAPI 文档的 linter,并且有很多规则来检查是否使用通用的规范来开发 RestfulAPI。要使用 spectral 非常简单,你只需要使用下面的命令安装 node package:
// using npm
npm install @stoplight/spectral
// using yarn
yarn add @stoplight/spectral
在执行命令 lint 并检查 API 是否符合规范之前,你需要像之前提到的那样打包 API,之后只需运行下面的命令。
spectral lint ./bin/api.yaml --ignore-unknown-format
之后,你可以根据输出信息来判断是否有发生错误,如下图所示。
- 光谱成功输出
- 频谱误差输出
如果你想了解 spectral 是如何工作的,lint 有哪些可用选项,以及如何创建自己的规则或忽略现有规则,我建议你访问官方文档,里面有很好的例子说明如何做到这一点。
或者你也可以使用 Apifox 导出标准的 OpenAPI 文档,帮助你便捷快速定位错误问题。
结论
这篇文章背后的想法只是展示如何使用 API-First 方法和 OpenAPI 规范开始设计 API。在下一篇文章中,我们将讨论模拟服务器和 SDK,以及我们如何利用 OpenAPI 来帮助改进开发过程。
本系列共有三篇,欢迎阅读其他文章: