优化 Swagger 的实现,提升可读性

This commit is contained in:
YunaiV 2022-11-12 01:22:43 +08:00
parent b4e68fa018
commit 3c5eff5603
3 changed files with 101 additions and 61 deletions

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.gateway.swagger; package cn.iocoder.yudao.gateway.swagger;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -9,55 +8,47 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.SecurityConfiguration; import springfox.documentation.swagger.web.*;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import springfox.documentation.swagger.web.SwaggerResourcesProvider; import javax.annotation.Resource;
import springfox.documentation.swagger.web.UiConfiguration; import java.util.List;
import springfox.documentation.swagger.web.UiConfigurationBuilder; import java.util.Optional;
/** /**
* Swagger Controller
*
* @author zxliu * @author zxliu
* @create 2022-10-25 11:24 * @date 2022-10-25 11:24
*/ */
@RestController @RestController
@RequestMapping("/swagger-resources") @RequestMapping("/swagger-resources")
public class SwaggerHandler { public class SwaggerHandler {
@Resource
private SwaggerResourcesProvider swaggerResources;
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") // 只有 @Autowired 可以实现可选注入
@Autowired(required = false) @Autowired(required = false)
private SecurityConfiguration securityConfiguration; private SecurityConfiguration securityConfiguration;
@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") // 只有 @Autowired 可以实现可选注入
@Autowired(required = false) @Autowired(required = false)
private UiConfiguration uiConfiguration; private UiConfiguration uiConfiguration;
private final SwaggerResourcesProvider swaggerResources;
@Autowired
public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
this.swaggerResources = swaggerResources;
}
@GetMapping("/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()),
HttpStatus.OK));
}
@GetMapping("/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@SuppressWarnings("rawtypes")
@GetMapping("") @GetMapping("")
public Mono<ResponseEntity> swaggerResources() { public Mono<ResponseEntity<List<SwaggerResource>>> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
} }
@GetMapping("/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(Optional.ofNullable(securityConfiguration)
.orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(Optional.ofNullable(uiConfiguration)
.orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
} }

View File

@ -1,48 +1,70 @@
package cn.iocoder.yudao.gateway.swagger; package cn.iocoder.yudao.gateway.swagger;
import java.util.ArrayList; import cn.hutool.core.collection.CollUtil;
import java.util.List; import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.config.GatewayProperties; import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.support.NameUtils; import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource; import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider; import springfox.documentation.swagger.web.SwaggerResourcesProvider;
/** import javax.annotation.Resource;
* @author zxliu import java.util.ArrayList;
* @create 2022-10-25 11:23 import java.util.HashSet;
*/ import java.util.List;
import java.util.Set;
/**
* Swagger 资源的 Provider 实现类
*
* @author zxliu
* @date 2022-10-25 11:23
*/
@Component @Component
@Primary @Primary
@AllArgsConstructor @Slf4j
public class SwaggerProvider implements SwaggerResourcesProvider { public class SwaggerProvider implements SwaggerResourcesProvider {
private final RouteLocator routeLocator; @Resource
private final GatewayProperties gatewayProperties; private GatewayProperties gatewayProperties;
/**
* 获得 SwaggerResource 列表
*
* @return SwaggerResource 列表
*/
@Override @Override
public List<SwaggerResource> get() { public List<SwaggerResource> get() {
// RouteDefinition 转换成 SwaggerResource
List<SwaggerResource> resources = new ArrayList<>(); List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>(); Set<String> serviceNames = new HashSet<>(); // 已处理的服务名避免重复
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId())); gatewayProperties.getRoutes().forEach(route -> {
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())) // 已存在的服务直接忽略
.forEach(route -> route.getPredicates().stream() String serviceName = route.getUri().getHost();
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName())) if (StrUtil.isEmpty(serviceName)) {
.forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(), return;
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0") }
.replace("**", "v2/api-docs")))) if (!serviceNames.add(serviceName)) {
); return;
}
// 获得 Path PredicateDefinition
String path = getRoutePath(route);
if (path == null) {
return;
}
// 重要构建最终的 SwaggerResource 对象
resources.add(buildSwaggerResource(serviceName, path));
});
return resources; return resources;
} }
private SwaggerResource buildSwaggerResource(String name, String location) {
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource(); SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name); swaggerResource.setName(name);
swaggerResource.setLocation(location); swaggerResource.setLocation(location);
@ -50,4 +72,31 @@ public class SwaggerProvider implements SwaggerResourcesProvider {
return swaggerResource; return swaggerResource;
} }
/**
* 获得路由的 Path
*
* 输入
* predicates:
* - Path=/admin-api/system/**
* 输出
* /admin-api/system/v2/api-docs
*
* @param route 路由
* @return 路由
*/
private String getRoutePath(RouteDefinition route) {
PredicateDefinition pathDefinition = CollUtil.findOne(route.getPredicates(),
predicateDefinition -> predicateDefinition.getName().equals("Path"));
if (pathDefinition == null) {
log.info("[get][Route({}) 没有 Path 条件,忽略接口文档]", route.getId());
return null;
}
String path = pathDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0");
if (StrUtil.isEmpty(path)) {
log.info("[get][Route({}) Path 的值为空,忽略接口文档]", route.getId());
return null;
}
return path.replace("/**", "/v2/api-docs");
}
} }

View File

@ -10,9 +10,9 @@ spring:
- id: system-admin-api # 路由的编号 - id: system-admin-api # 路由的编号
uri: grayLb://system-server uri: grayLb://system-server
predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
- Path=/admin-api/system/**,/captcha/** - Path=/admin-api/system/**
filters: filters:
- RewritePath=/admin-api/system/v2/api-docs, /v2/api-docs - RewritePath=/admin-api/system/v2/api-docs, /v2/api-docs # 配置,保证转发到 /v2/api-docs
- id: system-app-api # 路由的编号 - id: system-app-api # 路由的编号
uri: grayLb://system-server uri: grayLb://system-server
predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组 predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组