• Spring Cloud Gateway整合Swagger聚合微服务系统API文档(非Zuul)


    **【源码中的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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    添加测试Controller。

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

    配置Swagger使API注解生效。

    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
        @Bean
        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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    此时启动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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    添加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/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    因为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 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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    因为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> securityConfiguration() {
            return Mono.just(new ResponseEntity<>(
                    Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
        }
    
        @GetMapping("/configuration/ui")
        public Mono> uiConfiguration() {
            return Mono.just(new ResponseEntity<>(
                    Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
        }
    
        @GetMapping("")
        public Mono swaggerResources() {
            return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 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。

    //@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);
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在配置文件中为admin节点添加过滤器生效

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

    这时启动Gateway,访问gateway-ip:gateway-port/swagger-ui.html时,即可正常使用swagger。大家可以多加几个API服务试试效果

    最后附上效果图

  • 相关阅读:
    2-38 JSP01
    计算机毕业设计Java校园生活信息服务平台(源码+系统+mysql数据库+Lw文档)
    STM32初学-外部RTC时钟芯片DS3231
    C语言指针基础篇
    PHP与Java生成时间戳的区别
    敏捷开发模式下如何快速提升产品质量
    HTML5 Canvas 超逼真烟花绽放动画
    【Spring框架】Spring概述及基本应用
    河南省工业互联网大赛(复赛) wp
    Tomcat 源码解析一EL表达式源码解析
  • 原文地址:https://blog.csdn.net/m0_67401761/article/details/126496862