前言
一个好用的文档工具对于程序员开发、联调会提升很大效率。今天要介绍的就是一款我感觉很不错的文档工具 SpringDoc
,去年在上家公司就已经从 SpringFox
迁移,使用 SpringDoc
作为接口文档,感觉还是很不错的,但据我了解目前似乎并不普及。现公司使用的还是 SpringFox
,其实严格说来也只是引入了该组件,并未充分发挥它的作用。于是我决定正好更换一整套 swagger
环境,并且给同事推广一下 SpringDoc
......
SpringDoc 简介
SpringDoc
是继 SpringFox
之后一款优秀的 swagger
整合工具,首先它是新出来的(我这个人对新出来的技术有莫名的好感~~),其次相对于 SpringFox
而言,SpringDoc
先支持 OpenAPI3
、也支持 JSR303
规范、OAuth2、Spring WebFlux
等,下面让我们一起感受 SpringDoc
。当然任何一个新技术都推荐从官网学习,毕竟文章的内容大多来源于官网 SpringDoc 官网
从 SpringFox 迁移
如果要从 SpringFox
升级为 SpringDoc
,很简单只有三个步骤,
引入 SpringDoc 依赖
首先删掉 SpringFox
依赖,引入 SpringDoc
依赖, 因为我很早就给公司换了 SpringDoc
只是文章最近才写,所以还是选用我当时的版本 1.5.10
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.10</version>
</dependency>
复制代码
编写配置
只要注入 OpenAPI
的 Bean
即可
@Bean
public OpenAPI openApi( @Value("${spring.application.name}") String applicationName,ObjectProvider<BuildProperties> buildProperties) {
OpenAPI openAPI = new OpenAPI();
// 基本信息
openAPI.info(new Info().title(applicationName)
.description("服务名称")
.version(Optional.ofNullable(buildProperties.getIfAvailable()).map(BuildProperties::getVersion).orElse("1.0.0")));
return openAPI;
}
复制代码
注解更换
SpringDoc
稍微改动了相关注解,示例 SpringFox - SpringDoc
对应关系如下
@Api
→@Tag
@ApiIgnore
→@Parameter(hidden = true)
or@Operation(hidden = true)
or@Hidden
@ApiImplicitParam
→@Parameter
@ApiImplicitParams
→@Parameters
@ApiModel
→@Schema
@ApiModelProperty(hidden = true)
→@Schema(accessMode = READ_ONLY)
@ApiModelProperty
→@Schema
@ApiOperation(value = "foo", notes = "bar")
→@Operation(summary = "foo", description = "bar")
@ApiParam
→@Parameter
@ApiResponse(code = 404, message = "foo")
→@ApiResponse(responseCode = "404", description = "foo")
SpringDoc 初体验
进行了上面简单的迁移步骤,即可访问 http://localhost:8080/swagger-ui.html
文档页面,大致是这样
至此一个简单的 swagger
文档就做好了,SpringDoc
提供了一系列的配置,包括是否可用,swagger-ui
页面路径地址等等,在 application.yml
中输入 springdoc
查看自动提示
更多属性可参考官网 SpringDoc Properties ,Swagger Properties
自定义 Server 列表
默认情况下,Server
地址是获取的当前机器应用的 IP+Port
,但是现如今除开发环境外我们服务几乎都是在 Docker
容器中,这样一来自动获取的时候可能会拿到容器内部的 IP
或者 宿主机 IP
,此时使用 swagger
页面访问接口可能会发生 CORS
跨域错误。所以我们可以在向 Spring
容器注入 OpenAPI
时自己设置 server
列表
openAPI.servers(List.of(new Server().url("https://dev.gateway.yinsantech.cn/xx").description("xxx")));
复制代码
在请求中添加授权
绝大多数情况下我们项目都是需要认证的,比较常见的一种是在 HttpHeader
中传 token
,那么想在 swagger
页面添加 header
我们可以在向 Spring
注入 OpenAPI
时,配置使用 swagger
接口要携带哪些 header
。
@Bean
public OpenAPI openApi(@Value("${spring.application.name}") String applicationName, ObjectProvider<BuildProperties> buildProperties) {
OpenAPI openAPI = new OpenAPI();
//添加header
Map<String,SecurityScheme> map = new HashMap<>();
map.put("x-auth-token",new SecurityScheme().type(SecurityScheme.Type.APIKEY).in(SecurityScheme.In.HEADER).name("x-auth-token"));
openAPI.components(new Components().securitySchemes(map));
map.keySet().forEach(key -> openAPI.addSecurityItem(new SecurityRequirement().addList(key)));
// 基本信息
openAPI.info(new Info().title(applicationName)
.description("服务名称")
.version(Optional.ofNullable(buildProperties.getIfAvailable()).map(BuildProperties::getVersion).orElse("1.0.0")));
return openAPI;
}
复制代码
配置完之后页面上右上角会出现一个 Authorize
按钮,点击之后会出现输入 header
的弹框
我们可以发现 SecurityScheme.In.HEADER
枚举点进去之后还有 COOKIE,QUERY
也就是指定携带的 cookie 和查询参数。并不仅仅只能配置添加 header
但是仔细一想上述的硬编码显然不合适,因为我们不同的项目的 header
个数,名称都可能不一样,所以我们可以将它们配到配置文件中。首先自定义一个接受配置文件属性的类
@Component
@Data
@ConfigurationProperties(prefix = SwaggerProperties.PREFIX)
public class SwaggerProperties {
public static final String PREFIX = "swagger";
private Map<String, SecurityScheme> securitySchemes;
private List<Server> servers;
}
复制代码
然后在 application.yml中配置
swagger:
securitySchemes:
x-user-id:
type: APIKEY #类型
in: HEADER #放 header 里面
name: X-USER-ID # header - key
servers:
- url: http://127.0.0.1:8011/hive-collection-admin #服务器 URL
description: local
复制代码
针对不同的项目,配置不同的 header
个数,headerKey、server
地址等。
页面类替换显示
使用 swagger
之后,接口参数类型或者响应类都会被解析显示到页面上成为 schema
,但是有时候我们使用框架里面的类,有些属性并不希望他们显示出来,典型的就是自定义分页解析器的时候,参考我这篇文章 自定义参数解析器时多余字段显示 。此时 SpringDoc
给我们提供了一个功能是类型替换,把我们不希望显示的类替换成我们希望显示的类。
SpringDocUtils.getConfig().replaceWithClass(Page.class, PageRequest.class);
复制代码
值得注意的是 swagger
页面 Page
类型被替换成 PageRequest
,所以分页接口返回值要使用 IPage
类型,不能用 Page
,否则 swagger 页面显示也会被替换。
JSR 303 规范支持
如果对 JSR 303
规范还不清楚的可以参考我这篇文章 参数校验神器 hibernate-validator 配合统一异常处理 。当我们使用 JSR 规范注解校验参数的时候,例如
@Data
public class MessageSendRequest {
@Schema(description = "要发送的用户id和手机号")
@Size(min = 2)
private List<SmsUserSendInfo> userList;
@Schema(description = "产品 AP/CN")
@NotNull
private ProductEnum product;
}
复制代码
观察 swagger
页面上的 schema
看到 *
(必填) 、 minItem
、mininum
(最小值) 的提示了吧,再看我们点击 Try It Out
按钮后
swagger
给我们提供的默认请求体就已经列出了 List
元素最小个数 2
,example
最小值 10
,爱了爱了~~
Multiple Acceptable Schemas
咦,为什么这一栏的标题是英文呢?emmm 因为不知道该怎么描述,就把源码里面的关键注释搬过来了。。。
我举个例子,有些时候我们使用多态的思想开发,比如我现在接手的催收项目有 AP
的催收数据,CN
的催收数据,然后现在有一个催收详情接口,那 AP
和 CN
的催收详情页面显示的数据字段并不是完全一致的,但是它们又有一些公共字段。
当然我们可以选择定义两个 Dto
类,定义两个接口分别开发,但是这样不是很二吗。。所以嘛,我们可以定义一个父类存放公共字段,两个子类去继承扩展私有字段。
然后接口上定义一个类型区分一下两个产品的案件即可
@GetMapping("/{product}/{id}/detail")
public CaseDetailResponse detail(
@PathVariable("product") ProductEnum product, @PathVariable("id") Long id) {
return caseReviewerService.detail(product, id);
}
复制代码
接口返回值用父类兼容,看似很完美,但是查看 swagger
页面会发现,schema
上只有父类中的公共字段。。那么前端的小伙伴就没法知道 AP/CN
两个案件私有的字段是哪些了。这时候就要说到 Multiple Acceptable Schemas
。我们可以将两个 schema
都显示在页面上,通过以下配置
@Operation(
summary = "催收案件详情",
responses = {
@ApiResponse(
content = {
@Content(
schema = @Schema(oneOf = {ApCaseDetailResponse.class, CnCaseDetailResponse.class}))
})
})
复制代码
查看 swagger
页面会发现两个 schema
都列出来了
网关聚合
在现如今流行 SpringCloud
微服务的天下,通常我们一个项目可能会有很多微服务,以电商为例,订单、支付、商品、搜索、物流等等,如果每个微服务都是用一个单独的 url 地址去访问 swagger
页面,那体验也太差了。SpringDoc
提供了聚合功能,我们可以在 gateway
网关这里聚合所有的微服务 swagger
。以后浏览器就可以只收藏一个网关的 swagger
地址了。
上图是将所有的微服务 swagger
聚合到网关,想看哪个服务的 swagger
文档,Select a definition
即可。具体配置也很简单,首先肯定是要在网关引入 SpringDoc
依赖,然后配置聚合
springdoc:
api-docs:
enabled: false #生产环境关闭
swagger-ui:
enabled: false #生产环境关闭
urls-primary-name: gateway # 主 definition
urls:
- name: gateway
url: /v3/api-docs
- name: auth-center
url: /ac/v3/api-docs
#......
复制代码
不要忘记配置生产环境禁用 swagger
哈。。。
结语
本篇文章简单介绍了 SpringDoc
基本使用,以及常见的优化使用的方法。还有很多高级姿势例如整合 Security,WebFlux 以后用到的话再来更新。