最近使用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