Gateway整合Swagger
原文链接: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为注册中心,文章未展示多余部分)。
-
-
io.springfox -
springfox-swagger2 -
2.9.2
添加测试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,添加核心依赖包
-
-
org.springframework.cloud -
spring-cloud-starter-gateway -
-
org.springframework.boot -
spring-boot-starter-data-redis-reactive -
-
io.springfox -
springfox-swagger-ui -
2.9.2 -
-
io.springfox -
springfox-swagger2 -
2.9.2
添加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
get() { -
List
resources = new ArrayList<>(); -
List
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
> securityConfiguration() { - return Mono.just(new ResponseEntity<>(
- Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
- }
-
public Mono
> uiConfiguration() { - return Mono.just(new ResponseEntity<>(
- Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
- }
-
public Mono
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服务试试效果
最后附上效果图