Spring Cloud 之服务网关 Gateway(二) 集成 Swagger 组件

Spring Cloud 之服务网关 Gateway(二) 集成 Swagger 组件

概述

Swagger 是一个可视化 API 测试工具, 能够有效的构建强大的 Restful API 文档, 省去接口文档管理工作. 如果修改了代码, API 文档也会实时更新. 并且可以部分替代 Postman 用来调试接口

Spring Boot 整合了 swagger 组件, 使用也比较简单. 微服务随着项目的增加, 访问每一个应用的 swagger 显然是不合适的. 我们希望网关可以将所有的应用的 swagger 页面聚合起来. 这样前端只要访问网关的 swagger 的就可以了

Spring Cloud Gateway 整合 Swagger 会有一个麻烦, Gateway 底层是 WebFlux, 而 WebFlux 和 Swagger 不兼容. 所以不能通过一般的 Spring Boot 项目的方式简单的整合 Swagger, 否则启动的时候会报错. 常用的做法是自定义几个配置类来实现

编写简单案例

聚合模块说明

  • 版本说明

    Spring Boot : 2.0.9.RELEASE

    Spring Cloud: Finchley.RELEASE

  • 项目整体结构采用 maven 多 Module 结构

    模块 端口 说明
    Demo 父项目
    Eureka 15002 注册中心
    Gateway 15000 网关
    Comment 15003 应用服务
  • 项目树

    一个注册中心
    一个网关
    一个应用服务
    |_ demo
    	|_ eureka
    	|_ gateway
    	|_ comment
    	|_ pom.xml
    

编写 Eureka 服务

参考: Spring Cloud 之 Eureka 服务注册与发现

配置信息

编写应用程序服务 Comment-server

  • 编写 pom 文件

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
    </dependencies>
    
  • 编写工程的配置文件,

    eureka:
      client:
        service-url:
          defaultZone: http://localhost:15002/eureka/
      instance:
        lease-renewal-interval-in-seconds: 5
        lease-expiration-duration-in-seconds: 15
        perfer-in-address: true
    
    server:
      port: 15003
    
    spring:
      application:
        name: comment-server
      profiles:
        active: dev
    
  • 编写配置类

    @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("Spring Cloud China","http://springcloud.cn",""))//
            .version("2.0")//
            .build();
        }
    }
    
  • 编写控制器

    @RestController
    @RequestMapping("/comments")
    @Slf4j
    @Api("comments")
    public class CommentController {
          
          
    
        @Autowired
        private CommentService commentService;
    
        @ApiOperation(value = "获取评论详情", notes = "根据评论 id 获取详情")
        @ApiParam(value = "评论id")
        @GetMapping("/detail/{id}")
        public String getCommentDetails(@PathVariable("id") String id) {
          
          
            log.debug("Get comment by id [{}]", id);
            return id;
        }
    }
    
  • 验证效果

    服务启动后, 通过访问 http://localhost:15003/swagger-ui.html 访问 API 文档界面

    单项目swagger页面

编写 Gateway 网关服务

  • 编写 pom 文件

        <dependencies>
            <!-- 健康监控 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!-- Spring Cloud Gateway 依赖 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <!-- swagger -->
            <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>
        </dependencies>
    
  • application.yml 文件配置

    spring:
      application:
        name: gateway
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true     # 开启基于服务发现的路由规则
              lower-case-service-id: true   # 开启小写的 serviceId 进行基于服务路由的转发
          routes:
          - id: comment_server_route
            uri: lb://comment-server # 路由集群内其他服务,url需要用[lb://]+[serviceId]
            predicates:
            - Path=/comment/**
            filters:
            - SwaggerHeaderFilter
            - StripPrefix=1
        loadbalancer:
          retry:
            enabled: true # 内部已默认开启负载均衡
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:15002/eureka/  # 指定注册中心地址, 以便使用服务发现功能
      instance:
        lease-renewal-interval-in-seconds: 5
        lease-expiration-duration-in-seconds: 15
        perfer-in-address: true
    
    server:
      port: 15000
    
    management:
      endpoints:
        health:
          enabled: true
        gateway:
          enabled: true
        web:
          exposure:
            include: "*" # 暴露所有端点, 默认是 info, health
    
    logging:
      level:
        org.springframework.cloud.gateway: debug
    
  • 配置 GatewaySwaggerProvider, 获取 Api-doc, 即 GatewaySwaggerProvider

    @Component
    @Primary
    public class GatewaySwaggerProvider implements SwaggerResourcesProvider {
          
          
        public static final String API_URI = "/v2/api-docs";
        private final RouteLocator routeLocator;
        private final GatewayProperties gatewayProperties;
    
        public GatewaySwaggerProvider(RouteLocator routeLocator, GatewayProperties gatewayProperties) {
          
          
            this.routeLocator = routeLocator;
            this.gatewayProperties = gatewayProperties;
        }
    
        @Override
        public List<SwaggerResource> get() {
          
          
            List<SwaggerResource> resources = new ArrayList<>();
            List<String> routes = new ArrayList<>();
            // 取出 Spring Cloud Gateway 中的 route
            routeLocator.getRoutes()//
                .subscribe(route -> routes.add(route.getId()));
            // 结合 application.yml 中的路由配置, 只获取有效的 route 节点
            gatewayProperties.getRoutes().stream()//
                .filter(routeDefinition -> routes.contains(routeDefinition.getId()))//
                .forEach(routeDefinition -> routeDefinition.getPredicates().stream()//
                    .filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName().toLowerCase()))//
                    .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;
        }
    }
    
  • 编写 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)));
        }
    }
    
  • 配置过滤器

    (**Finchley.SR2 版本可跳过这一步, 配置文件里的 - SwaggerHeaderFilter 也不用配置 **)

    @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, GatewaySwaggerProvider.API_URI)){
          
          
                    return chain.filter(exchange);
                }
                String basePath = path.substring(0,path.lastIndexOf(GatewaySwaggerProvider.API_URI));
                ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME,basePath).build();
                ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
                return chain.filter(newExchange);
            };
        }
    }
    
  • 启动类

    @SpringBootApplication
    public class GatewayApplication {
          
          
        public static void main(String[] args) {
          
          
            SpringApplication.run(GatewayApplication.class, args);
        }
    }
    

发送请求测试效果

注册中心(eureka), 网关服务(gateway) 和应用服务(comment-server)依次启动后, 访问 http://localhost:15000/swagger-ui.html 可以看到 swagger 页面

gateway swagger页面

猜你喜欢

转载自blog.csdn.net/zjhcxdj/article/details/108622730
今日推荐