springcloud-gateway整合Swagger聚合微服务系统API文档
最近使用Spring Cloud Gateway替换Zuul的时候发现Swagger并不支持以WebFlux为底层的Gateway,无法集成,运行报错。
首先是子项目Spring Boot项目正常集成Swagger。在业务项目Admin中添加Swagger依赖包(使用consul为注册中心),这里跳过。
建立网关项目gateway,添加核心依赖包
buildscript { ext { springBootVersion = '2.3.8.RELEASE' } //... } dependencyManagement { imports { mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR10' } } compile 'org.springframework.cloud:spring-cloud-starter-config' compile 'org.springframework.cloud:spring-cloud-starter-consul-discovery' compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-gateway' compile group: 'org.springframework.boot', name: 'spring-boot-starter-webflux' compile group: 'org.springframework.boot', name: 'spring-boot-autoconfigure' compile group: 'org.springframework.boot', name: 'spring-boot-starter-logging' compile group: 'org.springframework.boot', name: 'spring-boot-starter-aop' compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator' compile group: 'org.springframework.boot', name: 'spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' //swagger implementation 'io.springfox:springfox-swagger2:2.9.2' implementation 'io.springfox:springfox-swagger-ui:2.9.2' implementation('com.github.xiaoymin:knife4j-spring-ui:2.0.8')
添加gateway路由配置
spring: application: name: gateway # 服务名称 cloud: consul: discovery: hostname: 本机ip config: name: ${spring.application.name} uri: http://ip:11111 profile: default label: master gateway: discovery: locator: enabled: true #表明gateway开启服务注册和发现的功能, 动态路由 lowerCaseServiceId: true filters: routes: - id: test # 唯一id ,随便起,不能重复 uri: lb://test # 匹配注册中心的服务 predicates: - Path=/test/** # 匹配的规则 filters: # 去掉路由前缀,访问 localhost:8088/test/v2/api 转发的就是 localhost:8089/v2/api # 1 : 代表剥离路径的个数 - StripPrefix=1
因为Swagger暂不支持webflux项目,所以Gateway里不能配置SwaggerConfig,也就是说Gateway无法提供自身API。但我想一般也不会在网关项目代码里写业务API代码吧。所以这里的集成只是基于基于WebMvc的微服务项目。
配置SwaggerProvider,获取Api-doc,即SwaggerResources。
@Component @Primary @AllArgsConstructor public class SwaggerProvider implements SwaggerResourcesProvider { public static final String API_URI = "/v2/api-docs"; private final RouteLocator routeLocator; private final GatewayProperties gatewayProperties; @Override public List<SwaggerResource> get() { List<SwaggerResource> resources = new ArrayList<>(); List<String> routes = new ArrayList<>(); //取出gateway的route routeLocator.getRoutes().subscribe(route -> routes.add(route.getId())); //结合配置的route-路径(Path),和route过滤,只获取有效的route节点 gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())) .forEach(routeDefinition -> routeDefinition.getPredicates().stream() .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName())) .forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(), predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0") .replace("/**", API_URI))))); return resources; } private SwaggerResource swaggerResource(String name, String location) { SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setName(name); swaggerResource.setLocation(location); swaggerResource.setSwaggerVersion("2.0"); return swaggerResource; } }
因为Gateway里没有配置SwaggerConfig,而运行Swagger-ui又需要依赖一些接口,所以我的想法是自己建立相应的swagger-resource端点。
@RestController @RequestMapping("/swagger-resources") public class SwaggerHandler { @Autowired(required = false) private SecurityConfiguration securityConfiguration; @Autowired(required = false) 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)); } @GetMapping("") public Mono<ResponseEntity> swaggerResources() { return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); } }
这时启动Gateway,访问gateway-ip:gateway-port/swagger-ui.html时,即可正常使用swagger。
补充一下SwaggerHeaderFilter类:
/** * @introduce: swagger请求头过滤器 * @author: lk * @date: 2020/6/4 **/ @Component public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory { private static final String HEADER_NAME = "X-Forwarded-Prefix"; @Override public GatewayFilter apply(Object config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); String path = request.getURI().getPath(); if (!StringUtils.endsWithIgnoreCase(path, SwaggerProvider.API_URI)) { return chain.filter(exchange); } String basePath = path.substring(0, path.lastIndexOf(SwaggerProvider.API_URI)); ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build(); ServerWebExchange newExchange = exchange.mutate().request(newRequest).build(); return chain.filter(newExchange); }; } }
参考:
https://blog.csdn.net/ttzommed/article/details/81103609?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-20.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-20.control
https://blog.csdn.net/qq_31748587/article/details/102563155
https://blog.csdn.net/lk1822791193/article/details/106540734?utm_medium=distribute.pc_relevant_download.none-task-blog-2~default~BlogCommendFromBaidu~default-1.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-2~default~BlogCommendFromBaidu~default-1.nonecas