记录下 OpenApi3 + SpringCloud Gateway 聚合文档的过程
组件选型
SpringDoc
Knife4j
SpringCloud Gateway
项目配置 在所有的 spring boot 项目中引入 SpringDoc
1 2 3 4 5 <dependency > <groupId > org.springdoc</groupId > <artifactId > springdoc-openapi-ui</artifactId > <version > ${springdoc.version}</version > </dependency >
在 gateway 项目中引入 SpringDoc
1 2 3 4 5 <dependency > <groupId > org.springdoc</groupId > <artifactId > springdoc-openapi-webflux-ui</artifactId > <version > ${springdoc.version}</version > </dependency >
并且需要排除 springdoc-openapi-ui 的依赖
OpenAPI 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Configuration @AllArgsConstructor public class SwaggerConfiguration { private final Environment environment; @Bean public OpenAPI openAPI () { return new OpenAPI () .info(info()); } private Info info () { return new Info () .title("xxxx" ) .description(environment.getProperty("spring.application.name" ) + " 服务 API 文档" ) .version("xx" ) .contact(new Contact ().name("xxx" ).url("xxx" ).email("xxxxx" )) .summary("OpenAPI 文档" ); } }
文档聚合 聚合 swagger 添加分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Component @AllArgsConstructor public class SwaggerConfig { public static final String MODULE_SUB_PREFIX = "ReactiveCompositeDiscoveryClient_" ; private final SwaggerUiConfigParameters swaggerUiConfigParameters; private final RouteLocator routeLocator; @Scheduled(fixedDelay = 20000) public void apis () { swaggerUiConfigParameters.getUrls().clear(); routeLocator.getRoutes().subscribe(routeDefinition -> { if (routeDefinition.getId().contains(MODULE_SUB_PREFIX)) { String name = routeDefinition.getId().substring(MODULE_SUB_PREFIX.length()); swaggerUiConfigParameters.addGroup(name); } }); } }
修改 /v3/api-docs/ 报文添加 basePath 使得 Knife4j 在聚合文档下能正常调试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Component public class AddBasePathFilterFactory extends AbstractGatewayFilterFactory <AddBasePathFilterFactory.Config> { private final ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory; public AddBasePathFilterFactory (ModifyResponseBodyGatewayFilterFactory modifyResponseBodyGatewayFilterFactory) { super (Config.class); this .modifyResponseBodyGatewayFilterFactory = modifyResponseBodyGatewayFilterFactory; } @Override public GatewayFilter apply (Config config) { ModifyResponseBodyGatewayFilterFactory.Config cf = new ModifyResponseBodyGatewayFilterFactory .Config() .setRewriteFunction(JsonNode.class, JsonNode.class, (e, jsonNode) -> Mono.justOrEmpty(addBasePath(e, jsonNode))); return modifyResponseBodyGatewayFilterFactory.apply(cf); } @Override public String name () { return "AddBasePath" ; } @Setter public static class Config { } private JsonNode addBasePath (ServerWebExchange exchange, JsonNode jsonNode) { if (jsonNode.isObject()) { ObjectNode node = (ObjectNode) jsonNode; String basePath = exchange.getRequest().getPath().subPath(4 ).value(); node.put("basePath" , basePath); return node; } return jsonNode; } }
网关路由配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 spring: cloud: gateway: routes: - id: openapi uri: http://localhost:${server.port} predicates: - Path=/v3/api-docs/** filters: - RewritePath=/v3/api-docs/(?<path>.*), /$\{path}/v3/api-docs - AddBasePath - id: doc uri: http://localhost:${server.port} predicates: - Path=/ filters: - RedirectTo=302, /doc.html