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对象]。描述预期的响应contentlinks
: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类型。声明属性是否为readOnlywriteOnly
:boolean类型。声明属性是否为writeOnlyxml
: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类型。命名空间URLprefix
: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 URLtokenUrl
:string类型,必选。用于此流程的Toke URLrefereshUrl
:string类型。用于获取refresh tokens的URLscopes
:Map[string, string],必选。用于OAuth2 Security Scheme的scope
Security Requirement 对象
- Security Requirement 对象:列出执行Operation所需的Security Schemes
{name}
:string数组。
上面列举的是OpenAPI文档中允许存在的所有数据结构,在实际编写过程中,通常并不会全部都有。同时纯手工根据规范编写文档是一件比较耗费力气的事情,事实上也有很多可视化的工具能够帮助我们方便地编写API信息,然后导出成对应的json
或yaml
文档。下面将会介绍相关的工具。
相关工具
与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 |
|
之后可以写一个简单的API接口,如下所示。可以看到在下面的接口中完成了两个方法,分别提供get和post请求的接口,以及对应的参数内容。
1 |
|
然后启动项目,我们可以直接访问对应的链接查看效果。默认情况下我们可以通过访问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 |
|
接下来是常用注解的介绍。通过在API实现的不同位置上标注对应注解,我们可以为这个API接口提供相关信息,以生成更加详细的接口文档。
@Tag(name="接口类描述")
:标注在Controller类上,表明这是一个Controller@Operation(summary="接口方法描述")
:标注在Controller方法上,表明这是一个API@Parameters(value={...})
:标注在Controller方法上,用于注明多个参数信息。- 其中value的值是一个注解数组,每个元素是
@Parameter
注解
- 其中value的值是一个注解数组,每个元素是
@Parameter(description="参数描述")
:可以用在上面的@Parameters
的value中,也可以直接标注在Controller方法的参数上@Schema
:可以标注在实体类或者实体类的属性上。如果这个实体类被用作请求的返回类型,则会在生成的文档中进行描述
下面用常用注解来对之前的简单Controller方法进行描述,用作示例:
1 |
|
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 |
|
这就引入的对应的插件。然后执行mvm clean complie
,Maven就会执行对应的生命周期,上面的插件对应的生成功能也会在过程中执行。执行完毕之后,就会生成对应的代码。但是需要注意的是,这里只会生成对应的代码,并不会引入相关的依赖。在代码中会使用到其他相关的依赖,还需要我们自己手动引入。