优化 Swagger 的实现,提升可读性
parent
b4e68fa018
commit
3c5eff5603
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 数组
|
||||||
|
|
Loading…
Reference in New Issue