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為注冊中心,文章未展示多余部分)。

  1.  
    <dependency>
  2.  
    <groupId>io.springfox</groupId>
  3.  
    <artifactId>springfox-swagger2</artifactId>
  4.  
    <version> 2.9.2</version>
  5.  
    </dependency>

添加測試Controller。

  1.  
    @RestController
  2.  
    @RequestMapping("/test")
  3.  
    @Api("測試")
  4.  
    public class TestController {
  5.  
     
  6.  
    @ApiOperation(value = "計算+", notes = "加法")
  7.  
    @ApiImplicitParams({
  8.  
    @ApiImplicitParam(name = "a", paramType = "path", value = "數字a", required = true, dataType = "Long"),
  9.  
    @ApiImplicitParam(name = "b", paramType = "path", value = "數字b", required = true, dataType = "Long")
  10.  
    })
  11.  
    @GetMapping("/{a}/{b}")
  12.  
    public Integer get(@PathVariable Integer a, @PathVariable Integer b) {
  13.  
    return a + b;
  14.  
    }
  15.  
    }

配置Swagger使API注解生效。

  1.  
    @Configuration
  2.  
    @EnableSwagger2
  3.  
    public class SwaggerConfig {
  4.  
    @Bean
  5.  
    public Docket createRestApi() {
  6.  
    return new Docket(DocumentationType.SWAGGER_2)
  7.  
    .apiInfo(apiInfo())
  8.  
    .select()
  9.  
    .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
  10.  
    .paths(PathSelectors.any())
  11.  
    .build();
  12.  
    }
  13.  
    private ApiInfo apiInfo() {
  14.  
    return new ApiInfoBuilder()
  15.  
    .title( "Swagger API")
  16.  
    .description( "test")
  17.  
    .termsOfServiceUrl( "")
  18.  
    .contact( new Contact("wd", "", ""))
  19.  
    .version( "2.0")
  20.  
    .build();
  21.  
    }
  22.  
    }

此時啟動Admin項目后應該能正常訪問admin-ip:admin:port/swagger-ui.html。下面是網關gateway部分。

建立網關項目gateway,添加核心依賴包

  1.  
    <dependency>
  2.  
    <groupId>org.springframework.cloud</groupId>
  3.  
    <artifactId>spring-cloud-starter-gateway</artifactId>
  4.  
    </dependency>
  5.  
    <dependency>
  6.  
    <groupId>org.springframework.boot</groupId>
  7.  
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
  8.  
    </dependency>
  9.  
    <dependency>
  10.  
    <groupId>io.springfox</groupId>
  11.  
    <artifactId>springfox-swagger-ui</artifactId>
  12.  
    <version> 2.9.2</version>
  13.  
    </dependency>
  14.  
    <dependency>
  15.  
    <groupId>io.springfox</groupId>
  16.  
    <artifactId>springfox-swagger2</artifactId>
  17.  
    <version> 2.9.2</version>
  18.  
    </dependency>

添加gateway路由配置

  1.  
    server:
  2.  
    port: 3333
  3.  
     
  4.  
    spring:
  5.  
    application:
  6.  
    name: wd-gateway
  7.  
    cloud:
  8.  
    gateway:
  9.  
    locator:
  10.  
    enabled: true
  11.  
    routes:
  12.  
    - id: wd-admin
  13.  
    uri: lb://wd-admin
  14.  
    predicates:
  15.  
    - Path=/admin/**
  16.  
    filters:
  17.  
    - SwaggerHeaderFilter
  18.  
    - StripPrefix=1
  19.  
     
  20.  
    eureka:
  21.  
    instance:
  22.  
    prefer-ip-address: true
  23.  
    client:
  24.  
    service-url:
  25.  
    defaultZone: http://localhost:8060/eureka/
  26.  
     

filter配置StripPrefix=1的意思:把path的前1個路徑截掉

因為Swagger暫不支持webflux項目,所以Gateway里不能配置SwaggerConfig,也就是說Gateway無法提供自身API。但我想一般也不會在網關項目代碼里寫業務API代碼吧。。所以這里的集成只是基於基於WebMvc的微服務項目。

配置SwaggerProvider,獲取Api-doc,即SwaggerResources。

  1.  
    @Component
  2.  
    @Primary
  3.  
    @AllArgsConstructor
  4.  
    public class SwaggerProvider implements SwaggerResourcesProvider {
  5.  
    public static final String API_URI = "/v2/api-docs";
  6.  
    private final RouteLocator routeLocator;
  7.  
    private final GatewayProperties gatewayProperties;
  8.  
     
  9.  
     
  10.  
    @Override
  11.  
    public List<SwaggerResource> get() {
  12.  
    List<SwaggerResource> resources = new ArrayList<>();
  13.  
    List<String> routes = new ArrayList<>();
  14.  
    //取出gateway的route
  15.  
    routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
  16.  
    //結合配置的route-路徑(Path),和route過濾,只獲取有效的route節點
  17.  
    gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
  18.  
    .forEach(routeDefinition -> routeDefinition.getPredicates().stream()
  19.  
    .filter(predicateDefinition -> ( "Path").equalsIgnoreCase(predicateDefinition.getName()))
  20.  
    .forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
  21.  
    predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
  22.  
    .replace( "/**", API_URI)))));
  23.  
    return resources;
  24.  
    }
  25.  
     
  26.  
    private SwaggerResource swaggerResource(String name, String location) {
  27.  
    SwaggerResource swaggerResource = new SwaggerResource();
  28.  
    swaggerResource.setName(name);
  29.  
    swaggerResource.setLocation(location);
  30.  
    swaggerResource.setSwaggerVersion( "2.0");
  31.  
    return swaggerResource;
  32.  
    }
  33.  
    }

因為Gateway里沒有配置SwaggerConfig,而運行Swagger-ui又需要依賴一些接口,所以我的想法是自己建立相應的swagger-resource端點。

  1.  
    @RestController
  2.  
    @RequestMapping("/swagger-resources")
  3.  
    public class SwaggerHandler {
  4.  
    @Autowired(required = false)
  5.  
    private SecurityConfiguration securityConfiguration;
  6.  
    @Autowired(required = false)
  7.  
    private UiConfiguration uiConfiguration;
  8.  
    private final SwaggerResourcesProvider swaggerResources;
  9.  
     
  10.  
    @Autowired
  11.  
    public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
  12.  
    this.swaggerResources = swaggerResources;
  13.  
    }
  14.  
     
  15.  
     
  16.  
    @GetMapping("/configuration/security")
  17.  
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
  18.  
    return Mono.just(new ResponseEntity<>(
  19.  
    Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
  20.  
    }
  21.  
     
  22.  
    @GetMapping("/configuration/ui")
  23.  
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
  24.  
    return Mono.just(new ResponseEntity<>(
  25.  
    Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
  26.  
    }
  27.  
     
  28.  
    @GetMapping("")
  29.  
    public Mono<ResponseEntity> swaggerResources() {
  30.  
    return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
  31.  
    }
  32.  
    }

【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。

  1.  
    //@Component
  2.  
    public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
  3.  
    private static final String HEADER_NAME = "X-Forwarded-Prefix";
  4.  
     
  5.  
    @Override
  6.  
    public GatewayFilter apply(Object config) {
  7.  
    return (exchange, chain) -> {
  8.  
    ServerHttpRequest request = exchange.getRequest();
  9.  
    String path = request.getURI().getPath();
  10.  
    if (!StringUtils.endsWithIgnoreCase(path, SwaggerProvider.API_URI)) {
  11.  
    return chain.filter(exchange);
  12.  
    }
  13.  
    String basePath = path.substring( 0, path.lastIndexOf(SwaggerProvider.API_URI));
  14.  
    ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
  15.  
    ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
  16.  
    return chain.filter(newExchange);
  17.  
    };
  18.  
    }
  19.  
    }

在配置文件中為admin節點添加過濾器生效

  1.  
    routes:
  2.  
    - id: wd-admin
  3.  
    uri: lb://wd-admin
  4.  
    predicates:
  5.  
    - Path=/admin/**
  6.  
    filters:
  7.  
    - SwaggerHeaderFilter
  8.  
    - StripPrefix=1

這時啟動Gateway,訪問gateway-ip:gateway-port/swagger-ui.html時,即可正常使用swagger。大家可以多加幾個API服務試試效果

最后附上效果圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM