诸如 Swagger UI、Slate 或 Spectacle 等项目提供了出色的文档功能,但侧重于展示单个 API - 但是,如果我们要通过单个门户清晰地提供多个 API 的文档怎么办?
随着支持公共 API 的团队数量的增长,或者需要向其他团队提供“内部目录”的团队数量的增长,拥有一个让用户了解各种 API 如何协同工作的场所至关重要。设计良好且维护良好的文档有助于提高采用率,强制执行使用服务的最佳实践,并且是展示新功能的绝佳方式。
在本系列中,我们将着眼于通过使用 React.js 动态地将 OAS 定义传递到 Swagger UI (第 1 部分)、自定义这个基本门户 (第 2 部分) 以及最终部署到外部主机 (第 3 部分) 来创建一个解决方案。本系列的后续部分将侧重于较小的功能 — 例如使用“演练”风格的页面增强文档,或连接到私有托管的定义。SwaggerHub 将充当我们的 API 规范的主机,尽管本系列稍后将探索加载它们的替代选项。
这些步骤适用于那些熟悉 React 和 OpenAPI 的人,但对于那些刚接触这些工具的人来说,它们也应该足够简单易懂。
最终门户的模板版本可以在此处找到,以启动此项目,并且可以在此处找到托管的运行示例。
什么是 OpenAPI、Swagger UI 或 SwaggerHub?
OpenAPI 规范 (OAS) 是一个 REST 定义标准,由 Linux 基金会的一部分 OpenAPI 计划维护。该规范最近发布了其 3.0 版本,并且其在整个行业中的采用和支持持续增长。像 OAS 这样的标准有助于促进“设计优先”或“定义驱动”的开发策略,让利益相关者在深入研究代码之前规划 API 及其功能。即将发生的变化和测试要求可以提前沟通,因为存在一个计划供每个人遵循。
Swagger UI 属于 Swagger 工具集,该工具集是支持使用 OAS 的开源项目集合。长期以来,它一直与定义并行使用,作为通过最小设置快速提供交互式文档的一种方式。我们将在单页应用程序中利用 Swagger UI 提供的 node 包。
SwaggerHub 是由 SmartBear 从头开始构建的平台解决方案,旨在大规模支持 OAS。它与由单个组织或团队管理多个不同 API 的想法相吻合,并且我们可以使用其后端根据需要将其加载到我们的文档门户中。通过使用像 SwaggerHub 这样的工具,我们可以更新和定义要向 API 使用者显示的“当前”规范,同时还支持正在进行的设计和开发。
环境设置
现在我们了解了我们将要使用的工具,让我们确保我们拥有构建项目所需的依赖项。在本系列的后面,我们将研究将其部署到外部主机,或在容器内运行 - 但现在让我们先使其在本地运行。这里的目标是以尽可能少的配置来完成此操作,以便保持我们的部署选项开放,因此我们只有一个开始要求:NodeJS。
通过运行 node -v
来验证您是否安装了 5.2.0 之后的节点版本,因为我们将使用 npx 来构建我们的初始起点。
我们将使用 create-react-app 项目作为起点,而不是组装整个 React 开发环境。这在一个命令中为我们提供了所有依赖项和基本结构。
要开始,请导航到将容纳您的项目的父文件夹并运行 npx create-react-app oas-doc-portal
NPX 将负责构建基本项目,您可以通过移动到新项目文件夹并启动默认应用程序来验证一切是否顺利。
$ cd oas-doc-portal
$ npm start

如果一切在浏览器中正确呈现,我们可以继续将其连接到我们的定义存储库。
这也是将项目连接到您选择的 GIT 存储库系统并进行初始推送的好时机。
呈现单个规范
在进行任何冒险的事情(例如动态传递定义)之前,让我们确保我们可以从远程资源加载和呈现单个定义。这将为我们提供一个坚实的工作起点,并使我们很容易识别何时不可避免地破坏某些东西。本教程将使用Swagger Petstore定义,可在此处找到:https://petstore.swagger.io/v2/swagger.json
所有的魔法都通过 swagger-ui node 包实现,因此让我们使用 ctrl+c
停止应用程序并将其添加到我们的应用程序中
- 运行
npm install swagger-ui
以将 swagger-ui 安装到项目中
- 打开
/src/App.js
- 在页面顶部的导入语句下添加 swagger-ui。
为了避免稍后重新配置它,我们可以将我们的构造函数添加到应用程序并为 OAS 链接创建一个状态。我们将使用新的 OAS 链接动态更新此值,因此它是保存引用的完美位置。本文更深入地探讨了 React 如何处理状态。
我们通过将定义链接传递给 swagger-ui 来呈现我们的文档。由于该链接将很快动态更新,我们应该利用 React 生命周期来保持一切最新 - 在这种情况下,componentDidMount
将完成这项工作(随着我们的需求变化,我们稍后会对此进行调整)。
Swagger UI 组件支持许多不同的参数,具体取决于我们希望它如何呈现,但对于我们的初始用例
- 我们告诉它在我们将要创建的
api-data
元素内部呈现我们的文档。
- 对存储在我们的状态对象中的 URL 的引用,告诉它从何处加载定义。
继续删除 create-react-app 为我们生成的占位符渲染元素,并用我们新定义的 div 要求替换它们
现在,Swagger UI 将在页面上查找 api-data
元素并在其中呈现我们的文档。如果我们启动应用程序并重新加载浏览器,我们应该看到之前的占位符页面现在具有 Swagger Petstore 的基本表示形式!

它不是很漂亮,但是数据在那里 — 并且我们现在可以手动更新我们的 definitionLink
值以呈现新的 API 数据。本质上,接下来的步骤将着眼于将其作为应用程序本身的一部分来完成。
此时的应用程序应如下所示
动态更新定义
我们门户的最终目标是显示 API 列表,在每个 API 之间单击,并在我们的主窗口中看到文档更新。为此,我们将在侧边栏中显示列表,并在用户单击列表时更新 definitionLink
值。
首先,我们需要考虑从哪里获取我们的 API 列表。在本教程中,我们将使用 SwaggerHub 作为我们的存储库系统来提供各种 API 定义。如果您还没有 SwaggerHub 帐户,可以在此处免费注册,创建一个组织并添加一些定义。或者,您可以使用本教程中提供的示例组织。
现在我们有了一个 SwaggerHub 组织和一些托管在那里的定义,我们可以开始考虑将它们拉入我们的门户。 这将全部由 SwaggerHub API 驱动,因此首先我们将编写一个新函数来处理我们不同的 API 调用,并避免重写太多代码。
- 向我们的应用程序添加一个新的
swaggerhub
函数
此函数将允许我们在几个不同的级别对 SwaggerHub 进行调用,并且应该足够灵活以支持我们在未来构建新功能。 接下来,我们希望查看获取 API 的“列表”。
SwaggerHub API 允许关于组织的请求,并且响应包含一个 API 名称数组和指向其位置的链接,这对于我们的用例来说是完美的。
让我们使用 swaggerhub
函数并创建一个新的 getOrganizationData
调用。 这将采用一个输入参数,即我们组织的名称,并将 SwaggerHub 的响应设置为状态级别的 API 列表。
现在更新状态对象以期望 definitionList
。 这也是添加稍后将使用的一对键/值对的好时机,我们应该将这两个新函数绑定到应用程序。
我们还在 getOrganizationData
调用中引用了一个组织,但尚未在任何地方存储该信息。 未来,我们还需要添加一个子标题和一个公司徽标,所以让我们创建一个配置文件来存储此信息。
- 在
/src
目录中,创建一个名为 organization_config.json
的新文件
- 将以下对象添加到配置文件中,并填写您组织的信息
- 接下来,将配置文件导入到
src/App.js
中
- 最后,创建一个
componentWillMount
函数来处理页面加载时的数据映射
我们已经构建了处理获取 API 列表的块 — 现在是时候开始考虑我们希望如何将该信息呈现给我们的消费者了。 接下来的几个步骤将构建一个新组件 Sidebar
,以及一个名为 apiLink
的新元素,该元素将在侧边栏内填充。
- 在
/src
中创建一个新文件 — Sidebar.js
。 侧边栏一开始将非常简单,只是一个基本结构和一些我们存储在配置文件中的显示名称数据
- 在主应用程序的
render
部分中,更新以包含新组件 — Sidebar
。 我们也希望在此处传递它需要的数据和函数
- 确保也将
Sidebar
导入到应用程序中。 我们还应该从 swagger-ui 模块中引入 CSS 文件,以稍微清理我们的文档视图。 我们将在稍后探讨如何自定义它。
- 为了稍微清理一下浏览器中的内容,让我们添加一些快速的 CSS,以清楚地表明我们正在进行更改的内容和位置。 在
App.css
文件中,我们可以开始组织我们的页面

太棒了!我们有一个侧边栏的基本结构(一个标题和一个将容纳链接的主体) — 但是我们缺少一个关键部分,即链接本身。我们已经通过传入的 props
传递了我们所需的大部分数据,所以现在只需编写一些逻辑来处理该数据并更新我们的状态对象。
- 创建一个新文件
/src/APILink.js
并使用它来处理通过侧边栏传递下来的数据
- 我们这里要求了一些尚未传递下来的东西。 首先,让我们在 App.js 中创建
updateDefintionLink
函数,在构造函数中绑定它并将其传递到侧边栏中
- 现在我们需要将两者连接起来,所以让我们更新
Sidebar.js
以包含以下内容
添加了什么?
- 首先,我们验证是否已加载组织数据,如果尚未加载,则使用
getOrganizationData
发出该请求。
- 一旦我们有了 API 数据,我们就会迭代它并将其输入到新的元素
APILink
中,这将根据用户的选择处理更新我们的 definitionLink
组件。我们检查它是否在 SwaggerHub 中“发布”,然后将链接推送到 apiLinks
数组。
- 最后,我们从
APILink.js
中导入了新的内容。
完美。 如果我们现在重新加载页面,我们应该看到我们的侧边栏中填充了我们所有不同 API 的名称! 让我们快速更新我们的 CSS 以清理它们
现在我们应该看到所有链接都显示在左侧,并且可以单击。 只有一个问题 — 文档组件实际上并没有更新! 幸运的是,这是一个快速修复。 现在,我们的 updateDefinitionLink
正在执行其工作(如果我们使用 console.log
,我们可以看到 URL 被正确传递) — Swagger UI 只是不知道它已更新,因为我们将其置于生命周期阶段。
要解决此问题,只需将我们早期的 componentDidMount
函数更新为 App.js
中的 componentDidUpdate
,当我们重新加载页面时,它应该会随着每个新的链接选择进行更新!

总结
至此,我们拥有了一个功能齐全(尽管相当基础)的 API 文档门户! 我们可以继续添加新的 API 版本或定义,更改我们后端上的发布和取消发布 — 并且我们的文档将始终保持最新。
在本系列的下一部分中,我们将研究如何有效地开始设置侧边栏和 Swagger UI 元素的样式以个性化文档,然后再在最后一部分中部署到几个不同的托管站点。
以下是此时我们应该拥有的最终代码。
感谢阅读!正在寻找更多 API 资源?订阅 Swagger 新闻通讯。每月接收包含我们最佳 API 文章、培训、教程等内容的电子邮件。 订阅