The service Gateway of Spring Cloud integrates Swagger components

Spring Cloud's service gateway gateway (II) integrates Swagger components

summary

Swagger is a visual API testing tool, which can effectively build powerful Restful API documents and save interface document management If the code is modified, the API document will be updated in real time And it can partially replace Postman to debug the interface

Spring Boot integrates swagger components and is easy to use With the increase of microservices, it is obviously inappropriate to access swagger of each application We hope that the gateway can aggregate the swagger pages of all applications In this way, the front end only needs to access the swagger of the gateway

Spring Cloud Gateway will have a problem for the whole Swagger. The underlying layer of the Gateway is WebFlux, which is incompatible with Swagger Therefore, Swagger cannot be simply integrated through the general Spring Boot project, otherwise an error will be reported when starting Common practice is to customize several configuration classes to implement

Write simple cases

Aggregation module description

  • Version Description

    Spring Boot : 2.0.9.RELEASE

    Spring Cloud: Finchley.RELEASE

  • The overall structure of the project adopts maven multi Module structure

    modular port explain
    Demo Parent project
    Eureka 15002 Registration Center
    Gateway 15000 gateway
    Comment 15003 application service
  • Project book

    A registry
     A gateway
     An application service
    |_ demo
    	|_ eureka
    	|_ gateway
    	|_ comment
    	|_ pom.xml
    

Write Eureka services

reference resources: Eureka service registration and discovery of Spring Cloud

configuration information

Write application service comment server

  • Write pom file

    <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>
    
  • Prepare the configuration file of the project,

    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
    
  • Write configuration class

    @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();
        }
    }
    
  • Write controller

    @RestController
    @RequestMapping("/comments")
    @Slf4j
    @Api("comments")
    public class CommentController {
    
        @Autowired
        private CommentService commentService;
    
        @ApiOperation(value = "Get comment details", notes = "According to comments id Get details")
        @ApiParam(value = "comment id")
        @GetMapping("/detail/{id}")
        public String getCommentDetails(@PathVariable("id") String id) {
            log.debug("Get comment by id [{}]", id);
            return id;
        }
    }
    
  • Verification effect

    After the service is started, access http://localhost:15003/swagger-ui.html access API document interface

Write Gateway gateway service

  • Write pom file

        <dependencies>
            <!-- Health monitoring -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!-- Spring Cloud Gateway rely on -->
            <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 file configuration

    spring:
      application:
        name: gateway
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true     # Enable routing rules based on service discovery
              lower-case-service-id: true   # Turn on the serviceId in lowercase for forwarding based on service routing
          routes:
          - id: comment_server_route
            uri: lb://Comment server # routes other services in the cluster. The url needs to use [lb://]+[serviceId]
            predicates:
            - Path=/comment/**
            filters:
            - SwaggerHeaderFilter
            - StripPrefix=1
        loadbalancer:
          retry:
            enabled: true # Load balancing is enabled internally by default
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:15002/eureka / # specify the registry address to use the service discovery feature
      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: "*" # Expose all endpoints. The default values are info and health
    
    logging:
      level:
        org.springframework.cloud.gateway: debug
    
  • Configure the GatewaySwaggerProvider and obtain the API doc, that is, the 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<>();
            // Take out the route in the Spring Cloud Gateway
            routeLocator.getRoutes()//
                .subscribe(route -> routes.add(route.getId()));
            // Combined with application The route configuration in YML only obtains valid route nodes
            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;
        }
    }
    
  • Write the swagger resource endpoint

    @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)));
        }
    }
    
  • Configure filter

    (* * this step can be skipped in the version of Finchley.SR2, and the SwaggerHeaderFilter in the configuration file does not need to be configured * *)

    @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);
            };
        }
    }
    
  • Startup class

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

Send request test effect

After the registration center (eureka), gateway service (gateway) and comment server (comment server) are started in turn, access http://localhost:15000/swagger-ui.html, you can see the swagger page

Tags: Spring Boot Spring Cloud api eureka gateway

Posted by Gomesh on Tue, 17 May 2022 11:37:00 +0300