作为一名开发者,往往须要编写程序的 API 文档,尤其是 Web 后端开发者,在跟前端对接 HTTP 接口的时候,一个好的 API 文档可能大大提高合作效率,升高沟通老本,本文就来聊聊如何应用 OpenAPI 构建 HTTP 接口文档。
OpenAPI
什么是 OpenAPI
OpenAPI 是规范化形容 API 畛域利用最宽泛的行业标准,由 OpenAPI Initiative 组织定义并保护,同时也是 Linux 基金会下的一个开源我的项目。它应用规定的格局来形容 HTTP RESTful API 的定义,以此来标准 RESTful 服务开发过程。应用 JSON 或 YAML 来形容一个规范的、与编程语言无关的 HTTP API 接口。
简略来说,OpenAPI 就是用来定义 HTTP 接口文档的一种标准,大家都依照同一套标准来编写接口文档,可能极大地缩小沟通老本。
OpenAPI 标准
OpenAPI 文档编写在一个 .json 或 .yaml 中,举荐将其命名为 openapi.json 或 openapi.yaml,OpenAPI 文档其实就是一个繁多的 JSON 对象,其中蕴含合乎 OpenAPI 标准中定义的构造字段。
OpenAPI 标准根本信息如下:OpenAPI 标准内容蕴含十分多的细节,本文无奈一一解说,这里仅介绍常见的根本信息,以 YAML 为例进行阐明。
一个 YAML 格局的 OpenAPI 文档示例如下:
openapi: 3.1.0info: title: Tic Tac Toe description: | This API allows writing down marks on a Tic Tac Toe board and requesting the state of the board or of individual squares. version: 1.0.0 # 此为 API 接口文档版本,与 openapi 版本无关 tags: - name: Gameplaypaths: # Whole board operations /board: get: summary: Get the whole board description: Retrieves the current state of the board and the winner. tags: - Gameplay operationId: get-board responses: "200": description: "OK" content: application/json: schema: $ref: "#/components/schemas/status" # Single square operations /board/{row}/{column}: parameters: - $ref: "#/components/parameters/rowParam" - $ref: "#/components/parameters/columnParam" get: summary: Get a single board square description: Retrieves the requested square. tags: - Gameplay operationId: get-square responses: "200": description: "OK" content: application/json: schema: $ref: "#/components/schemas/mark" "400": description: The provided parameters are incorrect content: text/html: schema: $ref: "#/components/schemas/errorMessage" example: "Illegal coordinates"...
如果你开发过 API 接口,置信能看懂文档大部分内容所代表的含意。不用齐全把握其语法,这并不会对浏览本文接下来的内容造成困扰,因为稍后我会介绍如何通过代码正文的形式主动生成此文档。如果你想手动编写 OpenAPI 文档,那我还是举荐你浏览下 OpenAPI 标准。浏览标准是一个比拟干燥的过程,如果你没有急躁读完,强烈建议浏览 OpenAPI 标准入门(https://oai.github.io/Documentation/),
相较于完整版的标准要精简得多,并且解说更加易于了解。
另外还举荐拜访 OpenAPI Map 网站来把握 OpenAPI 标准,该网站以思维导图的模式展示标准的格局以及阐明。
OpenAPI.Tools
当初咱们晓得了 OpenAPI 标准,接下来要做什么?当然是理解 OpenAPI 凋谢了哪些能力。
有一个叫 OpenAPI.Tools 的网站,分类整理并记录了社区围绕 OpenAPI 标准开发的风行工具。能够看到列表中有很多分类,在咱们日常开发中,最常常应用的有三类:
文档编辑器
文档编辑器不便咱们用来编写合乎 OpenAPI 标准的文档,有助于进步编写文档的效率,就像 VS Code 可能不便咱们编写代码一样。
文档编辑器有两种:文本编辑器以及图形编辑器。
文本编辑器举荐应用在线的 Swagger Editor,可能实现格局校验和实时预览 Swagger 交互式 API 文档性能,成果如下图所示:如果你习惯应用 VS Code,也有相应插件可供使用。图形编辑器的益处是可能以可视化的模式编辑内容,不理解 OpenAPI 标准语法也能编辑。能够依据本人爱好来进行抉择,如 Stoplight Studio、APIGit 等。
Mock 服务器
当咱们应用 OpenAPI 标准来进行接口开发时,往往采纳文档后行的策略,也就是前后端在开发代码前,先定义好接口文档,再进行代码的编写。此时前端如果想测试接口可用性,而后端代码还没有编写实现,Mock 服务器就派上用场了。Mock 服务器可能依据所提供的 OpenAPI 接口文档,主动生成一个模仿的 Web Server。应用 Mock 服务器可能轻松模仿实在的后端接口,不便前端同学进行接口调试。
下面提到的 APIGit 也同时具备此性能。
代码生成器
还有一种很实用的工具是代码生成器,代码生成器有两种类型:一种是从代码 / 正文生成 OpenAPI 文档,另一种是从 OpenAPI 文档生成代码。
这类工具同样十分多,且更为实用。比方咱们有一份写好了的 Go Web Server 代码,想要主动生成一份 OpenAPI 文档,就能够应用 go-swagger 这个工具来生成一份 openapi.yaml 文档。
而如果咱们有一份 openapi.yaml 文档,就能够利用 go-swagger 生成一份 Go SDK 代码,甚至它还能依据这份 OpenAPI 文档生成 Go Web Server 的框架代码,咱们只须要在对应的接口外面实现具体的业务逻辑即可。
Swagger
Swagger 是什么
Swagger 是一套围绕 OpenAPI 标准所构建的开源工具集,提供了弱小和易于应用的工具来充分利用 OpenAPI 标准,Swagger 工具集由最后的 Swagger 标准背地的团队所开发。Swagger 工具集提供了 API 设计、开发、测试、治理和监控等能力,其中最次要的工具蕴含如下三个:
- Swagger Codegen:依据 OpenAPI 标准定义生成服务器存根和客户端 SDK。
- Swagger Editor:基于浏览器的在线 OpenAPI 标准编辑器。
- Swagger UI:以 UI 界面的形式可视化展现 OpenAPI 标准定义,并且可能在浏览器中进行交互。
当然 Swagger 也有为企业用户提供的免费版本工具 SwaggerHub Enterprise,感兴趣的同学能够自行理解。
Swagger 和 OpenAPI 的关系
讲到了 Swagger,就不得不提及 Swagger 和 OpenAPI 的分割与区别,因为这二者常常在一起呈现。
在 OpenAPI 尚未呈现之前,Swagger 代表了 Swagger 标准以及一系列围绕 Swagger 标准的开源工具集。Swagger 标准最初一个版本是 2.0,之后就捐献给了 OAI 并被重新命名为 OpenAPI 标准,所以 OpenAPI 标准第一个版本是 2.0,也就是 Swagger 标准 2.0,而由 OAI 这个组织公布的第一个 OpenAPI 标准正式版本是 3.0.0。
当初,Swagger 标准已被 OpenAPI 标准齐全接管并取代。OpenAPI 代表了 OpenAPI 标准以及一系列生态,而 Swagger 则是这个生态中的一部分,是 Swagger 团队围绕 OpenAPI 标准所开发的一系列工具集。
Swagger 是 OpenAPI 生态中十分重要的组成部分,因为它给出了一整套计划,且十分风行。
OpenAPI 在理论开发的利用
接下来咱们聊聊以 Go 语言为例解说 OpenAPI 在理论开发中的利用。前文介绍了编写 OpenAPI 文档的两种编辑器:文本编辑器以及图形编辑器。在日常开发中,后端能够先应用这类编辑器如 Swagger Editor 编写出 OpenAPI 文档,而后将这份文档交给前端,前端拿到 OpenAPI 文档后将其导入到 Swagger Editor,就能够在线浏览接口文档并与之进行交互,之后前后端就能够并行开发了。
这样的工作流看起来仿佛没什么问题,不过编写 OpenAPI 文档毕竟是个苦力活,不仅有大量的反复工作,还要求开发者熟练掌握 OpenAPI 标准语法。这对于“爱偷懒”的开发者显然是无奈承受的,就像段子里说的,程序员最厌恶两件事:1. 写文档,2. 他人不写文档。而这个问题的解法,当然就是前文提到的代码生成器。
用 Swag 生成 Swagger 文档
在 Go 语言生态里,目前有两个比拟风行的开源工具能够生成 Swagger 文档,别离是 go-swagger 和 swag。它们都能依据代码中的正文生成 Swagger 文档,go-swagger 作为一款 OpenAPI.Tools 举荐的工具,其性能比 swag 更加弱小且 Github Star 数量也更高。不过本文将抉择 swag 来进行介绍,一是因为 swag 比拟轻量,更适宜微服务开发;二是如果应用 swag,那么正文代码会离接口代码更近,降级时不便保护。如果你有更高级的需要,如依据 Swagger 文档生成客户端 SDK,服务端存根等,则举荐应用 go-swagger。
留神:在这里我始终提到的都是生成 Swagger 文档,而没有说是 OpenAPI 文档。因为无论是 swag 还是性能更弱小的 go-swagger,它们目前都仅反对生成 OpenAPI 2.0 文档,并不反对生成 OpenAPI 3.0+ 文档,而 OpenAPI 2.0 版本咱们更习惯称其为 Swagger 文档。
装置 Swag
应用 go install 命令即可装置。
$ go install github.com/swaggo/swag/cmd/swag@latest # 装置
$ swag --version # 查看版本
swag version v1.8.10
Swag 命令行工具
swag 十分简洁,仅提供了两个次要命令 init 和 fmt。
- 在蕴含 main.go 文件(默认状况下)的我的项目根目录运行 swag init 命令,将会解析 swag 正文并生成 docs/ 目录以及 /docs/docs.go、docs/swagger.json、docs/swagger.yaml 三个文件。
$ swag init -h # 查看 init 子命令应用办法 NAME: swag init - Create docs.goUSAGE: swag init [command options] [arguments...]OPTIONS: --quiet, -q 不在控制台输入日志 (default: false) --generalInfo value, -g value API 通用信息所在的 Go 源文件门路,如果是相对路径则基于 API 解析目录 (default: "main.go") --dir value, -d value API 解析目录,多个目录可用逗号分隔 (default: "./") --exclude value 解析扫描时排除的目录,多个目录可用逗号分隔 --propertyStrategy value, -p value 构造体字段命名规定,三种:snake_case,camelCase,PascalCase (default: "camelCase") --output value, -o value 所有生成文件的输入目录(swagger.json, swagger.yaml and docs.go)(default:"./docs") --outputTypes value, --ot value 生成文件的输入类型(docs.go, swagger.json, swagger.yaml)三种:go,json,yaml (default: "go,json,yaml") --parseDependency, --pd 解析依赖目录中的 Go 文件 (default: false) --markdownFiles value, --md value 指定 API 的形容信息所应用的 Markdown 文件所在的目录,默认禁用 --parseInternal 解析 internal 包中的 Go 文件 (default: false) --generatedTime 输入工夫戳到输入文件 `docs.go` 顶部 (default: false) --parseDepth value 依赖项解析深度 (default: 100) --requiredByDefault 默认状况下,为所有字段设置 `required` 验证 (default: false) --instanceName value 设置文档实例名 (default: "swagger") --parseGoList 通过 'go list' 解析依赖关系 (default: true) --tags value, -t value 逗号分隔的标签列表,用于过滤指定标签生成 API 文档。非凡状况下,如果标签前缀是 '!' 字符,那么带有该标记的 API 将被排除 --help, -h 显示帮忙信息 (default: false)
留神:以上 swag init 命令可选参数介绍略有删减,只列出了罕用选项,更残缺的文档请参考官网 GitHub 仓库。
-
swag fmt 命令能够格式化 swag 正文。
$ swag fmt -h # 查看 fmt 子命令应用办法 NAME: swag fmt - format swag comments USAGE: swag fmt [command options] [arguments...] OPTIONS: --dir value, -d value API 解析目录,多个目录可用逗号分隔 (default: "./") --exclude value 解析扫描时排除的目录,多个目录可用逗号分隔 generalInfo value, -g value API 通用信息所在的 Go 源文件门路,如果是相对路径则基于 API 解析目录 (default: "main.go") --help, -h 显示帮忙信息 (default: false)
在 Gin 中应用 Swag
在 gin 框架可能很不便的应用 swag,步骤如下:
-
筹备我的项目目录构造如下:
. ├── go.mod ├── go.sum └── main.go
-
初始化我的项目
$ go mod init gin-swag
- 编写 main.go 代码如下:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// @title Swagger Example API
// @version 1.0
// @schemes http
// @host localhost:8080
// @BasePath /api/v1
// @tag.name example
// @tag.description 示例接口
// Helloworld godoc
//
// @Summary 该操作的简短摘要
// @Description 操作行为的具体阐明
// @Tags example
// @Accept json
// @Produce json
// @Success 200 {string} string "Hello World!"
// @Router /example/helloworld [get]
func Helloworld(g *gin.Context) {g.JSON(http.StatusOK, "Hello World!")
}
func main() {r := gin.Default()
v1 := r.Group("/api/v1")
{eg := v1.Group("/example")
{eg.GET("/helloworld", Helloworld)
}
}
if err := r.Run(":8080"); err != nil {panic(err)
}
}
代码中的正文局部即为 swag 的正文语法,稍后通过这些正文生成 Swagger 文档。
其中通用 API 信息局部正文含意如下:还有一部分正文代表了 API 操作,其含意如下:以上这些正文最终都会对应到 OpenAPI 2.0 标准的某个字段上。
- 应用 swag 依据正文生成 Swagger 文档,在我的项目根目录下(.)执行 swag init,将失去新的目录构造:
.
├── docs
│ ├── docs.go
│ ├── swagger.json
│ └── swagger.yaml
├── go.mod
├── go.sum
└── main.go
能够发现 swag init 生成的三个文件 docs.go、swagger.json、swagger.yaml 默认都在 docs/ 目录下。
其中 swagger.json、swagger.yaml 正是合乎 OpenAPI 2.0 标准的 JSON 和 YAML 接口文档,例如 swagger.yaml 内容如下:
basePath: /api/v1
host: localhost:8080
info:
contact: {}
title: Swagger Example API
version: "1.0"
paths:
/example/helloworld:
get:
consumes:
- application/json
description: 操作行为的具体阐明
produces:
- application/json
responses:
"200":
description: Hello World!
schema:
type: string
summary: 该操作的简短摘要
tags:
- example
schemes:
- http
swagger: "2.0"
tags:
- description: 示例接口
name: example
比照下面代码中的正文,很容易将其对应起来,相比于间接编写 YAML 格局文档,显然在代码中编写正文更为简略。
将其复制到 Swagger Editor 编辑器中即可查看 Swagger UI 预览。
将 Gin 作为 Swagger UI 服务器
下面咱们通过 Swag 生成了 Swagger 文档,并手动将生成的 swagger.yaml 复制到 Swagger Editor 编辑器进行 Swagger UI 预览。不过这么做显然有点麻烦,好在 swag 作者也思考到了这一点,所以他又提供了另外两个我的项目 gin-swagger 和 files,可能间接将 gin 作为 Swagger UI 服务器,这样就不必每次都将 swagger.yaml 手动复制到 Swagger Editor 编辑器能力实现 Swagger UI 预览。
应用步骤如下:
- 下载 gin-swagger、files
$ go get -u github.com/swaggo/gin-swagger
$ go get -u github.com/swaggo/files
- 在代码中导入 gin-swagger、files
import "github.com/swaggo/gin-swagger" // gin-swagger middleware
import "github.com/swaggo/files" // swagger embed files
- 注册 Swagger 文档路由地址
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
残缺代码如下:
package mainimport ("net/http" "github.com/gin-gonic/gin" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" "gin-swag/docs" // 以后包名为 gin-swag)// @title Swagger Example API// @version 1.0// @schemes http// @host localhost:8080// @BasePath /api/v1// @tag.name example// @tag.description 示例接口 // Helloworld godoc//// @Summary 该操作的简短摘要 // @Description 操作行为的具体阐明 // @Tags example// @Accept json// @Produce json// @Success 200 {string} string "Hello World!"// @Router /example/helloworld [get]func Helloworld(g *gin.Context) {g.JSON(http.StatusOK, "Hello World!")}func main() { // 会笼罩下面正文局部 title 属性的设置 docs.SwaggerInfo.Title = "Swag Example API" r := gin.Default() v1 := r.Group("/api/v1") {eg := v1.Group("/example") {eg.GET("/helloworld", Helloworld) } } // Swagger 文档接口地址 r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) if err := r.Run(":8080"); err != nil {panic(err) }}
- 执行 go run main.go 启动服务,拜访 http://localhost:8080/swagger/index.html 即可查看 Swagger UI 交互式文档界面。
这个本地的 Swagger UI 服务器同样反对交互式操作。
与前端对接时,咱们只须要将接口文档地址给到前端,前端就能够依据这个 Swagger UI 界面进行接口查阅和调试了,十分不便。
让 Swag 反对多版本 API 文档
理论工作中,咱们的我的项目会比这个只有一个接口的 demo 简单得多,同时 API 也可能会反对多版本,比方 /api/v1、/api/v2,针对这些场景,我实现了一个应用示例 https://github.com/jianghushinian/swag-example 供你参考。
为了更加不便地应用 swag 命令,在 swag-example 我的项目中,我把 swag fmt 和 swag init 命令都写在 Makefile 文件中,最重要的就是以下两条命令:
swag init -g internal/api/controller/v1/docs.go --exclude internal/api/controller/v2 --instanceName v1
swag init -g internal/api/controller/v2/docs.go --exclude internal/api/controller/v1 --instanceName v2
这两条命令别离生成 v1、v2 两个版本的 API 文档,这样能够将不同版本的接口离开展现,更加清晰。
其中 -g 参数指明 API 通用正文信息所在的 Go 源文件门路,大型项目中为了放弃代码架构整洁,这些正文应该独立于一个文件,而不是间接写在 main.go 中。
–exclude 参数指明生成 Swagger 文档时,须要排除的目录。能够发现,在生成 v1 版本接口文档时,我排除了 v2 接口目录,在生成 v2 版本接口文档时,排除了 v1 接口目录,这样就实现了多版本接口拆散。
对于这个我的项目的更多细节就留给你本人去摸索了,置信你浏览完代码后会有所播种。
Swag 应用倡议
在前文介绍的 swag 应用流程中,不晓得你有没有留神到,咱们是先编写的代码,而后再生成的 Swagger 文档,最初将这份文档交给前端应用。
这显然违反了「文档后行」的思维,理论工作中,咱们更多的时候是先跟前端约定好接口,而后后端提供 Swagger 文档供前端应用,最初才是前后端编码阶段。
要想解决这个问题,最间接的解决方案是不应用 swag 工具,而是间接应用 Swagger Editor 这种编辑器手写 Swagger 文档,这样就能实现文档后行了。
但这又违反了 OpenAPI 给出的「最佳实际」,举荐主动生成 Swagger 文档,而非手动编写。
我本人的解决方案是,仍旧抉择应用 swag 工具,不过在编写代码时,先写接口的框架代码,而不写具体的业务逻辑,这样就可能先通过接口正文生成 Swagger 文档,供前端应用,而后再编写业务代码。
另外,较为遗憾的是,目前 swag 生成的文档是 OpenAPI 2.0 版本,并不能间接生成 OpenAPI 3.0 版本,如果你想应用 OpenAPI 3.0 版本的文档,一个变通的办法是应用工具将 OpenAPI 2.0 文档转换成 OpenAPI 3.0,如前文提到的 Swagger Editor 就反对此操作。
应用 ReDoc 格调的 API 文档
兴许相较于 Swagger UI 多年不变的界面风格,你更喜爱 ReDoc 格调的 UI,那么 go-redoc 是一个比拟不错的抉择。
在 gin 中应用 go-redoc 非常简单,只须要将如下套路代码退出到咱们的 main.go 文件中即可。
import (
"github.com/gin-gonic/gin"
"github.com/mvrilo/go-redoc"
ginRedoc "github.com/mvrilo/go-redoc/gin"
)
...
doc := redoc.Redoc{
Title: "Example API",
Description: "Example API Description",
SpecFile: "./openapi.json", // "./openapi.yaml"
SpecPath: "/openapi.json", // "/openapi.yaml"
DocsPath: "/docs",
}
r := gin.New()
r.Use(ginRedoc.New(doc))
执行 go run main.go 启动服务,拜访 http://localhost:8080/redoc 即可查看 Redoc UI。不过,相较于 Swagger UI,Redoc UI 有个弊病是不能实现交互式操作,如果仅以此作为文档查阅工具,没有交互式操作的需要,那么还是比拟举荐应用的。
更先进的 API 工具举荐
除了 OpenAPI.Tools 举荐的开源工具,社区中其实还有很多其余优良工具值得尝试应用,比方我这里要举荐的一款国产工具 Apifox,官网将其定义为 Apifox = Postman + Swagger + Mock + JMeter,集 API 设计 / 开发 / 测试 于一身。
Apifox 堪称一站式图形化工具,其性能十分弱小,就像前文提到的 APIGit 同时具备了编辑器和 Mock 服务器的性能,Apifox 有过之而无不及。图形化工具上手难度不大,加上 Apifox 自身由国内开发,非常容易上手,所以本文也就不深刻介绍了,你能够观看官网教程 21 分钟学会 Apifox 来学习应用。心愿本文对你有所帮忙~
参考
OpenAPI 官网:https://www.openapis.org/
OpenAPI 入门:https://oai.github.io/Documentation/
OpenAPI 标准:https://spec.openapis.org/oas/latest.html
OpenAPI 标准中文版:https://openapi.apifox.cn/
OpenAPI 标准思维导图版:https://openapi-map.apihandyman.io/
OpenAPI.Tools:https://openapi.tools/
Swagger 官网:https://swagger.io/
swag:https://github.com/swaggo/swag
swag-example:https://github.com/jianghushinian/swag-example
go-redoc:https://github.com/mvrilo/go-redoc
Apifox 官网:https://www.apifox.com/