由于开发迭代导致api接口越来越多,前端对接时,或者想查看某个版本接口时带来很大麻烦,因此有了实现swagger-ui前端界面能手动选择要显示特定版本的想法。话不多说,先看效果(原理就是利用swagger的分组功能):
@ApiOperation(value = "上传文件@(v1.0.0)", notes = "上传文件")
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Result
其中@(v1.0.0)就是版本标记,如果出现版本迭代可以用逗号隔开,比如:@(v1.0.0,v1.1.0),这就标记了改api出现过迭代,在v1.1.0中出现过更改
@Bean
public Docket allGroup() {
Set setProtocols = new HashSet<>(Arrays.asList(protocols));
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo("all"))
.groupName("all")
.select()
.apis(basePackage(BASE_PACKAGES))
.apis(input -> matchVersion(input, null))
.paths(PathSelectors.any())
.build()
.protocols(setProtocols)
.securitySchemes(this.securitySchemes())
.securityContexts(this.securityContexts());
}
@Bean
public void otherGroups() {
Map versions = new HashMap<>(4);
Map handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
for (Map.Entry infoEntry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = infoEntry.getValue();
ApiOperation apiOperation = handlerMethod.getMethodAnnotation(ApiOperation.class);
if (Objects.nonNull(apiOperation)) {
String value = apiOperation.value();
if (StringUtils.isNotBlank(apiOperation.value())) {
String[] versionInfos = getVersions(value);
if (ArrayUtils.getLength(versionInfos) > 0) {
for (String versionInfo : versionInfos) {
versions.put(versionInfo.replace(".", ""), versionInfo);
}
}
}
}
}
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
for (Map.Entry version : versions.entrySet()) {
Docket docket = createDocket(version.getValue());
defaultListableBeanFactory.registerSingleton(version.getKey(), docket);
}
}
private static String[] getVersions(String version) {
String reg = "(.)*(@[\\((].*[\\))])(.)*";
if (version.matches(reg)) {
version = version.replaceAll(reg, "$2");
reg = "(@[\\((])(.*)([\\))])";
version = version.replaceAll(reg, "$2");
return version.split("[,,]");
}
return new String[]{};
}
package com.example.swagger.config;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.*;
/**
* @author zxiaozhou
* @date 2020-07-06 18:00
* @since JDK1.8
*/
@Slf4j
@Configuration
@EnableSwagger2
@RequiredArgsConstructor
public class Swagger2Config implements WebMvcConfigurer {
/**
* 指定要扫描的路径
*/
private final static String[] BASE_PACKAGES = new String[]{"com.example.swagger"};
private final ApplicationContext applicationContext;
/**
* 支持协议
*/
private final static String[] protocols = new String[]{"http"};
/**
* 请求头token键
*/
private final static String ACCESS_TOKEN = "Authorization";
/**
* 所有api分组信息
*
* @return Docket ${@link Docket}
* @author zxiaozhou
* @date 2020-07-06 18:52
*/
@Bean
public Docket allGroup() {
Set setProtocols = new HashSet<>(Arrays.asList(protocols));
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo("all"))
.groupName("all")
.select()
.apis(basePackage(BASE_PACKAGES))
.apis(input -> matchVersion(input, null))
.paths(PathSelectors.any())
.build()
.protocols(setProtocols)
.securitySchemes(this.securitySchemes())
.securityContexts(this.securityContexts());
}
/**
* 其他api分组信息
*
* @author zxiaozhou
* @date 2020-07-06 18:52
*/
@Bean
public void otherGroups() {
Map versions = new HashMap<>(4);
Map handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
for (Map.Entry infoEntry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = infoEntry.getValue();
ApiOperation apiOperation = handlerMethod.getMethodAnnotation(ApiOperation.class);
if (Objects.nonNull(apiOperation)) {
String value = apiOperation.value();
if (StringUtils.isNotBlank(apiOperation.value())) {
String[] versionInfos = getVersions(value);
if (ArrayUtils.getLength(versionInfos) > 0) {
for (String versionInfo : versionInfos) {
versions.put(versionInfo.replace(".", ""), versionInfo);
}
}
}
}
}
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
for (Map.Entry version : versions.entrySet()) {
Docket docket = createDocket(version.getValue());
defaultListableBeanFactory.registerSingleton(version.getKey(), docket);
}
}
/**
* 重写basePackage方法,使能够实现自定义多包扫描
*
* @param basePackages ${@link String[]}
* @return Predicate ${@link Predicate}
* @author zxiaozhou
* @date 2020-07-06 18:01
*/
public static Predicate basePackage(final String[] basePackages) {
return input -> declaringClass(input).transform(handlerPackage(basePackages)).or(true);
}
private static Function, Boolean> handlerPackage(final String[] basePackages) {
return input -> {
for (String basePackage : basePackages) {
boolean isMatch = input.getPackage().getName().startsWith(basePackage);
if (isMatch) {
return true;
}
}
return false;
};
}
private static Optional extends Class>> declaringClass(RequestHandler input) {
return Optional.fromNullable(input.declaringClass());
}
private List securitySchemes() {
List list = new ArrayList<>();
list.add(new ApiKey(ACCESS_TOKEN, ACCESS_TOKEN, "header"));
return list;
}
private List securityContexts() {
List list = new ArrayList<>();
list.add(SecurityContext.builder()
.securityReferences(this.defaultAuth())
.forPaths(PathSelectors.regex("^(?!auth).*$"))
.build());
return list;
}
private List defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List list = new ArrayList<>();
list.add(new SecurityReference(ACCESS_TOKEN, authorizationScopes));
return list;
}
/**
* 构建版本信息
*
* @param version ${@link String} 需要构建的版本
* @return Docket ${@link Docket} 版本Docket信息
* @author zxiaozhou
* @date 2020-07-08 18:47
*/
protected Docket createDocket(String version) {
Set setProtocols = new HashSet<>(Arrays.asList(protocols));
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo(version))
.groupName(version)
.select()
.apis(basePackage(BASE_PACKAGES))
.apis(input -> matchVersion(input, version))
.paths(PathSelectors.any())
.build()
.protocols(setProtocols)
.securitySchemes(this.securitySchemes())
.securityContexts(this.securityContexts());
}
/**
* 匹配swagger版本
*
* @param input ${@link String} 注解解析
* @param version ${@link String} 版本信息
* @return String[] ${@link String[]}
* @author zxiaozhou
* @date 2020-07-06 18:49
*/
protected static boolean matchVersion(RequestHandler input, String version) {
if (StringUtils.isBlank(version) && !isControllerHidden(input)) {
return true;
}
String[] versions = getVersions(input);
for (String v : versions) {
if (version.equalsIgnoreCase(v)) {
return true;
}
}
return false;
}
/**
* 获取版本信息
*
* @param input ${@link RequestHandler}
* @return String[] ${@link String[]} 获取的接口信息
* @author zxiaozhou
* @date 2020-07-06 18:11
*/
protected static String[] getVersions(RequestHandler input) {
if (Objects.isNull(input) || isControllerHidden(input)) {
return new String[]{};
}
Optional methodAnnotation = input.findAnnotation(ApiOperation.class);
if (methodAnnotation.isPresent()) {
ApiOperation apiOperation = methodAnnotation.get();
return getVersions(apiOperation.value());
}
return new String[]{};
}
/**
* 解析版本信息
*
* @param version ${@link String} 匹配版本
* @return String[] ${@link String[]} 匹配后的接口
* @author zxiaozhou
* @date 2020-07-06 18:02
*/
private static String[] getVersions(String version) {
String reg = "(.)*(@[\\((].*[\\))])(.)*";
if (version.matches(reg)) {
version = version.replaceAll(reg, "$2");
reg = "(@[\\((])(.*)([\\))])";
version = version.replaceAll(reg, "$2");
return version.split("[,,]");
}
return new String[]{};
}
/**
* 查询controller是否隐藏
*
* @param input ${@link RequestHandler}
* @author zxiaozhou
* @date 2020-07-20 12:48
*/
protected static boolean isControllerHidden(RequestHandler input) {
Optional controllerAnnotation = input.findControllerAnnotation(Api.class);
if (controllerAnnotation.isPresent()) {
Api api = controllerAnnotation.get();
return api.hidden();
}
return false;
}
/**
* api标题信息
*
* @param version ${@link String} 版本
* @return ApiInfo ${@link ApiInfo} api标题信息
* @author zxiaozhou
* @date 2020-07-08 18:45
*/
protected static ApiInfo apiInfo(String version) {
return new ApiInfoBuilder()
.title("Swagger API配置")
.description("接口详细说明")
.contact(new Contact("zxiaozhou", "", "[email protected]"))
.version(version)
.build();
}
}
代码中BASE_PACKAGES就是定义要扫描的包路径,这个是一个数组变量
Gitee代码仓库地址:[email protected]:zxiaozhou/swagger.git