swagger接口文档分组
解析问题
因项目中的接口越来越多,使用swagger调试接口的时候,因接口太杂太乱,不太容易找到想要调试的接口,所以研究了一下swagger分组的方法
但是分组是需要配置多个Docket的bean,而一般配置,就是一个docket一个函数定义并加上@Bean注解,代码冗余太多,所以就想着用灵活的方式去配置swagger分组。
@Configuration
public class SwaggerConfig {
/**
* 创建API
*/
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.OAS_30)
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描指定包
.apis(RequestHandlerSelectors.basePackage("com.xxx.xxx"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.pathMapping(pathMapping)
.groupName("全部");
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo() {
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("标题:xxx系统_接口文档")
// 描述
.description("描述:用于管理xxxxx信息,具体包括XXX,XXX模块...")
// 作者信息
.contact(new Contact("xxxx", null, null))
// 版本
.version("版本号:" + "1.0")
.build();
}
}
就像普通的swagger配置,一个docket包含所有接口, 而这样的话,swagger文档看起来就非常的杂乱,所以分组是个不错的选择
但是一般的分组是多个Docket去配置,比如:
@Bean
public Docket createRestApi1() {
return new Docket(DocumentationType.OAS_30)
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描指定包
.apis(RequestHandlerSelectors.basePackage("com.xxx.xxx"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.pathMapping(pathMapping)
.groupName("分组1");
}
@Bean
public Docket createRestApi2() {
return new Docket(DocumentationType.OAS_30)
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描指定包
.apis(RequestHandlerSelectors.basePackage("com.xxx.xxx"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.pathMapping(pathMapping)
.groupName("分组2");
}
...
一个分组就要复制一个docker定义,如果分组比较多的话,冗余代码太多,而且看起来也不是很美观,所以我才在想如果灵活配置的方式
分析简化方案
首先我想的就是,这里面重复代码这么多,能不能将这些重复代码去掉。
所以我想出了一个简单的方案,就是将不同的定义信息封装成一个定义信息集,而通过循环的方式将不同的信息当作变量放入。
于是我想到了这样一个方式,通过循环定义信息集,动态的去创建Docket并注册到Bean工厂中,但是这样的话,就需要操作spring Bean工厂,于是我又写了一个工具类,去操作spring bean工厂,然后通过这种方式实现了这个想法
实现简化方案
swagger的分组我是通过扫描不同的包来分的组,但是也可以使用url去区分
@Bean
public Docket createRestApi1() {
return new Docket(DocumentationType.OAS_30)
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描所有
.apis(RequestHandlerSelectors.any())
// 扫描指定url
.paths(PathSelectors.ant("/xxx/**"))
.build()
.pathMapping(pathMapping)
.groupName("分组1");
}
swagger定义信息 我使用的是枚举定义的,当然,也可以使用对象集合这样
package com.we.web.core.config;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import lombok.Getter;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.PathSelectors;
import java.util.function.Predicate;
/**
* swagger分组
*/
@Getter
public enum SwaggerEntity {
/** 系统管理 */
SYSTEM("系统",
basePackage("com.we.web.controller"),
PathSelectors.any()),
/** 后台 */
OPERATION("后台",
basePackage("com.we.operation.controller"),
PathSelectors.ant("/we-api/operation/**")),
/** 小程序 */
APPLET("小程序",
basePackage("com.we.operation.controller.applet", "com.we.operation.controller.wx"),
PathSelectors.any()),
;
SwaggerEntity(String groupName, Predicate<RequestHandler> apis, Predicate<String> paths) {
this.groupName = groupName;
this.apis = apis;
this.paths = paths;
}
/** 分组名称 */
private final String groupName;
/** 扫描包 */
private final Predicate<RequestHandler> apis;
/** 扫描包 */
private final Predicate<String> paths;
private static com.google.common.base.Predicate<RequestHandler> basePackage(String... basePackages) {
return input -> declaringClass(input).transform(handlerPackage(basePackages)).or(true);
}
private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
return Optional.fromNullable(input.declaringClass());
}
private static Function<Class<?>, Boolean> handlerPackage(String[] basePackage) {
return input -> {
// 循环判断匹配
for (String strPackage : basePackage) {
boolean isMatch = input.getPackage().getName().startsWith(strPackage);
if (isMatch) {
return true;
}
}
return false;
};
}
}
basePackages为何定义为数据类型是因为一个Docket可能要扫描多个包,如不需要,则改为String类型就可
下面就开始最关键的注入代码, 因需要项目启动就注入进去,所以使用@PostConstruct来使服务启动完就执行
/**
* 注入自定义swagger分组
*
* @author liac
* @date 2022/3/9 14:01
*/
@PostConstruct
public void importDocketGroup() {
// 循环SwaggerEntity中的所有定义
for (SwaggerEntity value : SwaggerEntity.values()) {
// 注册bean
Docket docket = SpringUtils.registerBean(null, Docket.class, Lists.newArrayList(DocumentationType.OAS_30), null);
// 执行docket的一些定义
docket // 是否启用Swagger
.enable(enabled)
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 设置SwaggerEntity中定义的需要扫描的包
.apis(value.getApis())
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(value.getPaths())
.build()
// 设置分组名
.groupName(value.getGroupName())
// 设置安全模式,swagger可以设置访问token
.securitySchemes(securitySchemes())
.securityContexts(securityContexts())
.pathMapping(pathMapping);
}
}
关于SpringUtils工具类请移步 spring bean工具类