原文鏈接:https://blog.csdn.net/ttzommed/article/details/81103609
【源碼中的Spring Boot版本為2.1.3,更新了一點小細節,主要是看思路吧】最近在學習SpringBoot2和Spring Cloud.Finchley版,網上資料也是少的可憐,大部分還是通過一些github或者碼雲上的一些開源框架來學習,途中出現的一些bug也只能自己看看源碼嘗試解決。最近使用Spring Cloud Gateway替換Zuul的時候發現Swagger並不支持以WebFlux為底層的Gateway,無法集成,運行報錯。下面分享我愚鈍的解決思路,和關鍵代碼,若有改進之處,望大佬指點,詳細代碼可以下載源碼查看。
貼上源碼https://gitee.com/wxdfun/sw
首先是子項目Spring Boot項目正常集成Swagger。在業務項目Admin中添加Swagger依賴包(使用Eureka為注冊中心,文章未展示多余部分)。
-
<dependency>
-
<groupId>io.springfox</groupId>
-
<artifactId>springfox-swagger2</artifactId>
-
<version> 2.9.2</version>
-
</dependency>
添加測試Controller。
-
-
-
-
public class TestController {
-
-
-
-
-
-
-
-
public Integer get(
-
return a + b;
-
}
-
}
配置Swagger使API注解生效。
-
-
-
public class SwaggerConfig {
-
-
public Docket createRestApi() {
-
return new Docket(DocumentationType.SWAGGER_2)
-
.apiInfo(apiInfo())
-
.select()
-
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
-
.paths(PathSelectors.any())
-
.build();
-
}
-
private ApiInfo apiInfo() {
-
return new ApiInfoBuilder()
-
.title( "Swagger API")
-
.description( "test")
-
.termsOfServiceUrl( "")
-
.contact( new Contact("wd", "", ""))
-
.version( "2.0")
-
.build();
-
}
-
}
此時啟動Admin項目后應該能正常訪問admin-ip:admin:port/swagger-ui.html。下面是網關gateway部分。
建立網關項目gateway,添加核心依賴包
-
<dependency>
-
<groupId>org.springframework.cloud</groupId>
-
<artifactId>spring-cloud-starter-gateway</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>io.springfox</groupId>
-
<artifactId>springfox-swagger-ui</artifactId>
-
<version> 2.9.2</version>
-
</dependency>
-
<dependency>
-
<groupId>io.springfox</groupId>
-
<artifactId>springfox-swagger2</artifactId>
-
<version> 2.9.2</version>
-
</dependency>
添加gateway路由配置
-
server:
-
port: 3333
-
-
spring:
-
application:
-
name: wd-gateway
-
cloud:
-
gateway:
-
locator:
-
enabled: true
-
routes:
-
- id: wd-admin
-
uri: lb://wd-admin
-
predicates:
-
- Path=/admin/**
-
filters:
-
- SwaggerHeaderFilter
-
- StripPrefix=1
-
-
eureka:
-
instance:
-
prefer-ip-address: true
-
client:
-
service-url:
-
defaultZone: http://localhost:8060/eureka/
-
filter配置StripPrefix=1的意思:把path的前1個路徑截掉
因為Swagger暫不支持webflux項目,所以Gateway里不能配置SwaggerConfig,也就是說Gateway無法提供自身API。但我想一般也不會在網關項目代碼里寫業務API代碼吧。。所以這里的集成只是基於基於WebMvc的微服務項目。
配置SwaggerProvider,獲取Api-doc,即SwaggerResources。
-
-
-
-
public class SwaggerProvider implements SwaggerResourcesProvider {
-
public static final String API_URI = "/v2/api-docs";
-
private final RouteLocator routeLocator;
-
private final GatewayProperties gatewayProperties;
-
-
-
-
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端點。
-
-
-
public class SwaggerHandler {
-
-
private SecurityConfiguration securityConfiguration;
-
-
private UiConfiguration uiConfiguration;
-
private final SwaggerResourcesProvider swaggerResources;
-
-
-
public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
-
this.swaggerResources = swaggerResources;
-
}
-
-
-
-
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
-
return Mono.just(new ResponseEntity<>(
-
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
-
}
-
-
-
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
-
return Mono.just(new ResponseEntity<>(
-
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
-
}
-
-
-
public Mono<ResponseEntity> swaggerResources() {
-
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
-
}
-
}
【Spring Boot版本超過2.0.6的應該可以跳過這一步,最新源碼也更新了。Spring修復了bug給我們添加上了這個Header】另外,我發現在路由為admin/test/{a}/{b},在swagger會顯示為test/{a}/{b},缺少了admin這個路由節點。斷點源碼時發現在Swagger中會根據X-Forwarded-Prefix這個Header來獲取BasePath,將它添加至接口路徑與host中間,這樣才能正常做接口測試,而Gateway在做轉發的時候並沒有這個Header添加進Request,所以發生接口調試的404錯誤。解決思路是在Gateway里加一個過濾器來添加這個header。
-
//@Component
-
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
-
private static final String HEADER_NAME = "X-Forwarded-Prefix";
-
-
-
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);
-
};
-
}
-
}
在配置文件中為admin節點添加過濾器生效
-
routes:
-
- id: wd-admin
-
uri: lb://wd-admin
-
predicates:
-
- Path=/admin/**
-
filters:
-
- SwaggerHeaderFilter
-
- StripPrefix=1
這時啟動Gateway,訪問gateway-ip:gateway-port/swagger-ui.html時,即可正常使用swagger。大家可以多加幾個API服務試試效果
最后附上效果圖