OpenAPI,Swagger以及相关工具

OpenAPI

简介

在日常的项目开发工作中,经常需要完成API的设计,实现和应用等工作。一个好的习惯是在实际进行代码开发之前就将API的相关信息设计好,否则在开发过程中进行API的返工,将会牵连到各方,造成较大的麻烦。这也就是API优先的设计,指的是API先于实现。因此,我们需要使用一个语言无关的方式来描述API的设计。利用API优先的设计,我们可以避免不必要的API改动,提升开发效率。

OpenAPI就是这样一种规范,它定义一种标准的,与具体编程语言无关的RESTful API的规范。在这种规范下,人类和计算机都能在不借助其他源代码和文档的前提下,理解同一个API的作用。同时由于API文档遵循标准的规范,也会有很多相关的工具来辅助进行工作,例如代码生成工具,文档展示工具等。

OpenAPI的项目地址为:OpenAPI-Github

文档规范

设计API的过程实际上就是编写OpenAPI文档的过程,最终结果是一个被各方认可的OpenAPI文档。OpenAPI的文档是一个json或者yaml格式的文件,它遵循一定标准的数据结构,其中描述了API的详细信息。一份OpenAPI文档可以是单个文件也可以被拆分为多个文件,可以使用ref来相互使用。推荐将根OpenAPI文档命名为openapi.json或者openapi.yaml

下面将介绍在一个OpenAPI文档中存在的所有数据结构。一个对象中可以包含多个字段,每个字段的值可能是某个字面量,也可能是另一个对象,或者对象列表。

OpenAPI 对象

  • OpenAPI 对象:是OpenAPI 文档的根对象
    • openapi:string类型,必选。标识当前使用的Open API 版本号
    • info:Info对象,必选。提供API相关的元数据
    • servers:Server对象数组,提供到服务器的连接信息。默认值为url/的Server对象
    • paths:Paths对象,必选。描述API的路径和操作
    • components:Components对象,其中包含多种结构
    • security:Secutiry Requirement对象数组,声明API使用的安全机制
    • tags:Tag对象数组,提供一系列标签
    • externalDocs:External Documentation对象,是附加的文档

Info 对象

  • Info 对象:提供API的元数据,这些元数据可能会被呈现在编辑工具或者文档生成工具中
    • title:stirng类型,必选。标识应用的名称
    • description:string类型。对应用的简单描述
    • termsOfService:string类型,指向服务条款的URL地址
    • contact:Contact对象,标识对应的联系人信息
    • license:License对象,标识对应的证书信息
    • version:string类型,必选。标识API的版本信息

Contact 对象

  • Contact 对象:联系人信息
    • name:string类型,联系人名称
    • url:string类型,联系人信息的URL地址
    • email:string类型,联系人的email地址

License 对象

  • License 对象:证书信息
    • name:string类型,必选。证书名称
    • url:string类型,证书的URL地址

Server 对象

  • Server 对象:标识一个服务器对象
    • url:string类型,必选。指向目标主机的URL地址,可以使用模版变量{xxx}
    • description:string类型,简单描述
    • variables:Map类型,KV分别为string和Server Variable对象。这些值可以用来替换服务器URL地址内的模板参数

Server Variable对象

  • Server Variable 对象:Server中URL地址模板变量替换的对象
    • enum:string数组类型,一组可以枚举的字符串值,表示允许的可替换选项
    • default:string类型,必选。在没有指定可替换值时的默认值
    • description:对变量的简单描述

Paths 对象

  • Paths 对象:定义各个API的相对路径以及操作,与Server对象中的URL地址组成完整的URL地址
    • /{path}:Path Item对象。这里的字段名表示相对路径,值表示对应API的相关信息

Path Item 对象

  • Path Item 对象:描述对一个路径可执行的有效操作
    • $ref:string类型。指定对此路径定义的外部引用。引用的格式也需要满足Path Item的格式
    • summary:string类型。对该API的简要总结,描述此路径内包含的所有操作
    • description:string类型。对该API的详细说明字符串
    • get:Operation 对象。定义适用于此路径的GET操作
    • put:Operation 对象。定义适用于此路径的PUT操作
    • post:Operation 对象。定义适用于此路径的POST操作
    • delete:Operation 对象。定义适用于此路径的DELETE操作
    • options:Operation 对象。定义适用于此路径的OPTIONS操作
    • head:Operation 对象。定义适用于此路径的HEAD操作
    • patch:Operation 对象。定义适用于此路径的PATCH操作
    • trace:Operation 对象。定义适用于此路径的TRACE操作
    • servers:Server 对象数组。此处的server也可使用此路径的所有操作
    • parameters:Parameter 对象。可用于此路径下所有操作的参数列表

Operation 对象

  • Operation 对象:描述对路径的某个操作
    • tags:string数组。控制API文档的标签列表,用于逻辑上的分组
    • summary:string类型,对该操作行为进行简单描述
    • description:string类型,对该操作行为的详细解释
    • externalDocs:External Documentation对象。表示附加的外部文档
    • operationId:string类型。用于表示此操作的唯一字符串,推荐在命名时符合一般的编程命名习惯
    • parameters:Parameter 对象数组。定义可用于此操作的参数列表
    • requestBody:Request Body对象。可用于此操作的请求体
    • responses:Responses 对象,必选。定义执行此操作后可能的响应值列表
    • callbacks:Map[string, Callback对象]。定义可能出现的回调映射
    • deprecated:boolean类型。标识此操作是否已经被废弃
    • security:Security Requirement对象数组。声明可用于此操作的安全机制
    • servers:Server对象数组。定义可以用于此操作的额外的Server 数组

Parameter 对象

  • Parameter 对象:描述一个操作参数。一个参数的唯一性由name和location的组合来确定
    • name:string类型,必选。参数的名称,区分大小写。
    • in:string类型,必选。表示参数的位置,可选的值有[query, header, path, cookie]
    • description:string类型。对该参数的简短描述
    • required:boolean类型。表示该参数是否是必选参数
    • deprecated:boolean类型。表示该参数是否被弃用
    • allowEmptyValue:boolean类型,设置是否允许传递空参数
    • style:String类型。描述根据参数值类型的不同如何序列化参数
    • explode:boolean类型。表示是否对数组内的值或者对象的键值对生成带分隔符的参数值
    • allowReserved:boolean类型,表示是否不允许使用%编码保留字符
    • schema:Schema对象,定义适用于此参数的类型结构
    • example:Any。不同媒体类型的示例
    • examples:Map[string, Example对象]。不同媒体类型的示例

Components 对象

  • Components 对象:描述各种文档中的可重用对象。此处定义的组件只有在被其他对象引用后才会产生效果。下面所有Map中value都可以是Reference对象,表示对外部的引用
    • schemas:Map[string, Schema]类型。定义可重用的Schema对象
    • responses:Map[string, Response]类型。定义可重用的Response对象
    • parameters:Map[string, Parameter]类型。定义可重用的Parameter对象
    • examples:Map[string, Example]类型。定义可重用的Example对象
    • requestBodies:Map[string, Request Body]类型。定义可重用的Request Body对象
    • headers:Map[string, Header]类型。定义可重用的Header对象
    • securitySchemes:Map[string, Security Scheme]类型。定义可重用的Security Scheme对象
    • links:Map[string, Link]类型。定义可重用的Link对象
    • callbacks:Map[string, Callback]类型。定义可重用的Callback对象

External Documentation 对象

  • External Documentation 对象:外部资源文档
    • description:对引用外部文档的简短描述
    • url:string类型,必选。表示外部文档的URL地址

Request Body 对象

  • Request Body 对象:定义请求体
    • description:string类型,对请求体的简要描述
    • content:Map[string, Media Type对象],必选。表示请求体的内容。key是一个媒体类型,值是对应的示例数据
    • required:boolean类型。表示请求体是否应该被包含在请求中,默认值为false

Media Type 对象

  • Media Type 对象:每种媒体类型对象都有相应的结构和示例来进行描述
    • schema:Schema对象。定义此媒体类型的结构
    • example:Any。媒体类型的示例
    • examples:Map[string, Example对象]。媒体类型的示例
    • encoding:Map[string, Encoding对象]。属性名与编码信息的映射

Encoding 对象

  • Encoding 对象:编码定义。一个编码定义仅适用于一个结构属性
    • contentType:string类型。描述对具体属性的Content-Type编码
    • headers:Map[string, Header对象]。提供附加信息的请求头键值对映射
    • style:string类型。描述一个属性根据它的类型将会被如何序列化
    • explode:boolean类型。表示是否对数组内的值或者对象的键值对生成带分隔符的参数值
    • allowReserved:boolean类型,表示是否不允许使用%编码保留字符

Responses 对象

  • Responses 对象:描述一个操作可能发生的响应的响应码与对应的响应体
    • defalut:Response对象。用于描述未被明确声明的HTTP响应码的对应响应
    • HTTP status Code:Response对象。这里的字段名为HTTP状态码,值为对应的响应

Response 对象

  • Response 对象:描述单个响应
    • description:string类型,必选。对响应的简短描述
    • headers:Map[string, Header对象]。HTTP头名称和定义的映射
    • content:Map[string, Media Type对象]。描述预期的响应content
    • links:Map[string, Link对象]

Callback 对象

  • Callback 对象:描述回调对象
    • {expression}:Path Item对象。用于定义回调请求和响应

Example 对象

  • Example 对象:示例对象
    • summary:string类型。该示例的简单描述
    • description:string类型。该示例的详细描述
    • value:Any。
    • externalValue:string类型

Header 对象

  • Header 对象:Header对象与Paramter 对象基本一致。但是有一定的限制
    • name不能被指定
    • in不能被指定

Tag 对象

  • Tag 对象:
    • name:string类型,必选。Tag的名称
    • description:string类型。Tag的简短描述
    • externalDocs:ExternalDocumentation对象。外部文档

Reference 对象

  • Reference 对象:用于引用规范内部或者外部的对象
    • $ref:string类型,必选。用于找到引用的对象位置

Schema 对象

  • Schema 对象:用于定义输入和输出的数据类型
    • nullable:boolean类型。是否允许发送null值
    • discriminator:Discriminator对象
    • readOnly:boolean类型。声明属性是否为readOnly
    • writeOnly:boolean类型。声明属性是否为writeOnly
    • xml:XML对象。
    • externalDocs:External Documentation对象。外部文档
    • example:Any。示例
    • deprecated:boolean类型。是否废弃

Discriminator 对象

  • Discriminator 对象:用于辅助序列化,反序列化和校验
    • propertyName:string类型,必选。在payload中表示discriminator值属性的名称
    • mapping:Map[string, string]。在payload中值与schema名称的映射

XML 对象

  • XML 对象:为XML模型定义的元数据对象
    • name:string类型。
    • namespace:string类型。命名空间URL
    • prefix:string类型。name前缀
    • attribute:boolean类型。
    • wrapped:boolean类型。

Security Scheme 对象

  • Security Scheme 对象:用于Operation的Security Scheme
    • type:string类型,必选。有效值包括[apiKey, http, oauth2, openIdConnect]
    • description:string类型。简单描述
    • name:string类型,必选。用于header,query或者cookie的参数名称
    • in:string类型,必选。API key的位置。有效值包括[query, header, cookie]
    • scheme:string类型,必选。用于HTTP Auahorization scheme的名称
    • bearerFormat:string类型。用于提示客户端所使用的bearer token的格式
    • flows:OAuth Flows对象,必选。包含所支持的Flow Types的配置信息
    • openIdConnectUrl:string类型,必选。用于发现OAuth2的配置值

OAuth Flows 对象

  • OAuth Flows 对象:配置支持的OAuth Flow
    • implicit:OAuth Flow对象。OAuth Implicit flow的配置
    • password:OAuth Flow对象。OAuth Resource Owner Password flow的配置
    • clientCredentials:OAuth Flow对象。OAuth Client Credentials flow的配置
    • authorizationCode:OAuth Authroization Code flow的配置

OAuth Flow 对象

  • OAuth Flow 对象:OAuth Flow的配置详情
    • authorizationUrl:string类型,必选。用于此流程的Authorization URL
    • tokenUrl:string类型,必选。用于此流程的Toke URL
    • refereshUrl:string类型。用于获取refresh tokens的URL
    • scopes:Map[string, string],必选。用于OAuth2 Security Scheme的scope

Security Requirement 对象

  • Security Requirement 对象:列出执行Operation所需的Security Schemes
    • {name}:string数组。

上面列举的是OpenAPI文档中允许存在的所有数据结构,在实际编写过程中,通常并不会全部都有。同时纯手工根据规范编写文档是一件比较耗费力气的事情,事实上也有很多可视化的工具能够帮助我们方便地编写API信息,然后导出成对应的jsonyaml文档。下面将会介绍相关的工具。

相关工具

与OpenAPI相关的工具有很多,涉及到API开发的各个生命周期。在OpenAPI.tools网站中可以看到相关的工具,我们通常使用的有以下几类工具:

  • OpenAPI的文本编辑器:例如Stoplight,它提供一个网页的编辑器帮助我们快速创建OpenAPI文档
  • Mock服务器:例如Prism。这是一个Mock服务器,由于使用相同的规范,因此API的消费者无需等待后台实现,而是可以使用随机的数据来进行前端的测试
  • 代码生成工具:例如openapi-generator。由于API使用的是OpenAPI规范,所以可以据此进行代码生成。API的提供者可以用工具来快速生成代码的骨架
  • 文档生成工具:例如swagger UI,它可以将我们的文档展示为对应的网页形式,可读性更高

还有其他相关的工具,包括OpenAPI文档的验证,文档解析,测试等,在需要使用时可以参考上面提到的工具网站。

Swagger

简介

Swagger本身是一个API文档的维护组织,后来成为了OpenAPI标准的主要定义者。OpenAPI就是由这个组织定义并贡献开源的。当然除此之外,Swagger也发布过其他标准,例如Swagger 2,Swagger3等,而OpenAPI的版本直接从3.0开始。Swagger2的包名为io.swagger,Swagger3的包名为io.swagger.core.v3

除此以外,Swagger还提供了一系列工具,用于生成,描述,调用和可视化RESTful风格的Web服务。这些工具用来帮助实现OpenAPI规范,在API生命周期的不同阶段使用。例如有:

  • Swagger Editor:用于在浏览器中编辑OpenAPI规范,并实时预览文档。当然Swagger编辑器除了能够支持OpenAPI规范,还支持其他规范,如Swagger 2.0等
  • Swagger UI:从符合OpenAPI规范的描述文件中生成可视化的文档,使用html进行展示,界面优美,效果直观
  • Swagger Codegen:从符合OpenAPI规范的描述文件中生成API客户端代码

除了使用这些工具之外,在日常的工作中,我们主要也会将Swagge集成到项目中进行使用,例如集成到SpringBoot项目中。所以下面就将介绍Swagger相关工具的使用,以及如何在SpringBoot项目中使用Swagger的相关工具。

SpringDoc

概念辨析

这里首先需要厘清一些相关概念,Swagger提供了相关工具使我们在API开发的各个阶段更加便捷,但是它本身还是单独分离的工具,是不能直接应用在SpringBoot项目当中的。而SpringFox和SpingDoc就是两个项目,帮助我们在SpringBoot项目中集成Swagger服务。

SpringFox(SpringFox by springfox)是 spring 社区维护的一个项目(非官方),它能够帮助使用者将 swagger2 集成到 Spring 中。常常用于 Spring 中帮助开发者生成文档,并可以轻松的在SpringBoot中使用。它支持多种规范,包括Swagger2,Swagger3,OpenAPI3等。但是该项目目前基本已经停止更新了。

SpringDoc(OpenAPI 3 Library for spring-boot (springdoc.org))也是 spring 社区维护的一个项目(非官方),帮助使用者将Swagger3 集成到 Spring 中。同样也可以用来在 Spring 中帮助开发者生成文档,并可以轻松的在SpringBoot中使用。相比于SpringFox,SpringDoc虽然只支持OpenAPI3,但是SpringDoc项目目前更加活跃,文档也更加全面。因此这里我们直接使用SpringDoc来集成OpenAPI的支持到SpringBoot项目中。

SpringDoc完成的功能是通过我们的API代码来生成对应的API文档。我们的API是通过类和方法来实现的。在类和方法上标注对应的注解之后,SpringDoc就可以借助扫描对应的注解来生成API文档,当然使用的是OpenAPI的规范。

Getting Started

首先在SpringBoot项目的pom.xml文件中增加SpringDoc的依赖如下:

1
2
3
4
5
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.14</version>
</dependency>

之后可以写一个简单的API接口,如下所示。可以看到在下面的接口中完成了两个方法,分别提供get和post请求的接口,以及对应的参数内容。

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class HelloController {
@GetMapping("/getHello/{name}")
public String getHello(@PathVariable String name) {
return String.format("Get Hello %s", name);
}

@PostMapping("/postHello/{id}")
public String postHello(@PathVariable Integer id) {
return String.format("Post Hello %d", id);
}
}

然后启动项目,我们可以直接访问对应的链接查看效果。默认情况下我们可以通过访问http://server:port/{context-path}/swagger-ui.html来访问UI界面,如下所示。访问地址可以在项目配置文件application.yaml中进行设置,设置项为springdoc.swagger-ui.path。例如springdoc:swagger-ui:path:/api。不过这只是一个映射,最终还是会被重定向到上面的地址。

可以看到SpringDoc自动为我们生成了前端文档的展示页面,其中包括了我们刚才写的两个接口。包括请求方式,请求参数等。同时点开详细信息,还可以直接模拟调用请求,得到返回结果。

同时SpringDoc还自动生成了OpenAPI的规范文档,分别提供json和yaml两种格式,可以通过下面的链接进行访问:

  • json:http://server:port/context-path/v3/api-docs
  • yaml:http://server:port/context-path/v3/api-docs.yaml

配置以及常用注解

从上面快速开始的案例可以看到,只要我们引入了SpringDoc的依赖,不需要额外添加其他的信息,它就能够自动扫描我们的类代码,生成相关的文档以及UI展示。但是这种展示能够提供的信息还是非常简单和有限的,如果需要更加详细的信息,还需要使用对应的配置类以及相关注解。

首先是配置类的介绍,在配置类中我们可以提供整个项目的相关信息。可以创建如下的类,首先在主类上标注@Configuration,表示这是一个配置类。之后在其中提供一个方法,返回值为OpenAPI类型,并使用@Bean标注方法,将返回值交由Spring管理。我们可以通过OpenAPI对象的各种方法来指定不同的信息。

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class OpenAPIConfig {
@Bean
public OpenAPI openApi(){
return new OpenAPI().info(new Info()
.title("openapi test title")
.description("test description")
.version("1.0.1")
);
}
}

接下来是常用注解的介绍。通过在API实现的不同位置上标注对应注解,我们可以为这个API接口提供相关信息,以生成更加详细的接口文档。

  • @Tag(name="接口类描述"):标注在Controller类上,表明这是一个Controller
  • @Operation(summary="接口方法描述"):标注在Controller方法上,表明这是一个API
  • @Parameters(value={...}):标注在Controller方法上,用于注明多个参数信息。
    • 其中value的值是一个注解数组,每个元素是@Parameter注解
  • @Parameter(description="参数描述"):可以用在上面的@Parameters的value中,也可以直接标注在Controller方法的参数上
  • @Schema:可以标注在实体类或者实体类的属性上。如果这个实体类被用作请求的返回类型,则会在生成的文档中进行描述

下面用常用注解来对之前的简单Controller方法进行描述,用作示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
@Tag(name = "I am a Hello Controller", description = "Hello, description")
public class HelloController {
@Operation(summary = "controller function: get Hello")
@Parameters(value = {
@Parameter(name = "p-name")
})
@GetMapping("/getHello/{name}")
public String getHello(@PathVariable String name) {
return String.format("Get Hello %s", name);
}

@Operation(summary = "controller function: post hello")
@PostMapping("/postHello/{id}")
public String postHello(@PathVariable @Parameter(name = "p-id") Integer id) {
return String.format("Post Hello %d", id);
}
}

Spring Swagger-CodeGen

上面我们已经展示了在项目中集成SpringDoc,让其为我们生成对应的API文档以及UI展示。不过一篇详细的文档,它需要我们利用众多的注解来标识,提供足够的注解信息才能生成。这些注解虽然并不是很复杂,但还是比较繁琐。于是,另一种利用Swagger的思想就是先得到一篇API文档,然后通过这个文档直接辅助生成对应的代码。也就是Swagger提供的Swagger Codegen服务。

我们可以将Swagger Codegen服务集成到SpringBoot项目中,这需要使用swagger-codegen-maven-plugin插件。插件的官方地址为swagger-api/swagger-codegen(github.com),其中详细描述了各种配置产生。下面开始介绍插件的相关使用。

为了使用插件,我们在在pom.xml文件中加入如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.swagger.codegen.v3</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>3.0.36</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/api.yaml</inputSpec>
<language>java</language>
<configOptions>
<sourceFolder>src/gen/java/main</sourceFolder>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

这就引入的对应的插件。然后执行mvm clean complie,Maven就会执行对应的生命周期,上面的插件对应的生成功能也会在过程中执行。执行完毕之后,就会生成对应的代码。但是需要注意的是,这里只会生成对应的代码,并不会引入相关的依赖。在代码中会使用到其他相关的依赖,还需要我们自己手动引入。

参考文章

  1. OpenAPI规范-中文版
  2. Spring Boot整合Swagger3(OpenAPI3)生成接口文档

OpenAPI,Swagger以及相关工具
http://example.com/2023/02/17/OpenAPI-Swagger以及相关工具/
作者
EverNorif
发布于
2023年2月17日
许可协议