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