Demand background
spring cloud builds a micro service system. Each business module uses the swagger open document interface to query. The swagger document aggregation query interface is provided in the business gateway module, which can be viewed by selecting the business module classification.
Frame selection, version and main functions
- spring boot 2.1.6.RELEASE
- spring cloud Greenwich.SR3
- spring cloud gateway 2.1.3.RELEASE gateway component
- knife4j 2.0.1 enhances the swagger ui style, and the gateway uses its starter dependency
- swagger bootstrap ui 1.9.6 enhanced swagger ui style
- spring4all-swagger 1.9.0.RELEASE configures swagger parameters, eliminating code development
Division of module responsibilities
-
swagger component
Develop a swagger spring boot starter in the project and integrate swagger bootstrap ui 1.9.6 and spring4all swagger 1.9.0 Release, providing @ EnableSwagger annotation service externally -
Business module
Reference the customized swagger spring boot starter, and add the basic swagger information configuration of this module in the configuration file. -
Gateway module
Reference knife4j to integrate swagger, and develop filter, handler and config to aggregate multi module swagger
Example of development steps
swagger component
pom.xml file dependency
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.6</version> </dependency> <dependency> <groupId>com.spring4all</groupId> <artifactId>swagger-spring-boot-starter</artifactId> <version>1.9.0.RELEASE</version> </dependency>
Custom annotation
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @EnableSwagger2Doc @EnableSwaggerBootstrapUI @Import(SwaggerCommandLineRunner.class) public @interface EnableSwagger { }
Note that @ EnableSwagger2Doc, @ enableswaggerbootstrap UI annotation, @ EnableSwagger2Doc annotation can document the swagger configuration and avoid the business module from redeveloping the code of swagger, @ enableswaggerbootstrap UI improves the swagger ui interface.
Specifies the default access port for swagger
@Slf4j @Component public class SwaggerCommandLineRunner implements CommandLineRunner { @Value("${server.port:8080}") private String serverPort; @Override public void run(String... args) { log.info("swagger url:http://localhost:" + serverPort + "/doc.html"); } }
In this way, the integration of this component is completed. This component exists in the public component of the project in the form of multiple module s, which can be referenced by maven.
Business module development
Use @ EnableSwagger annotation in application (self-developed one, don't make a mistake)
@SpringBootApplication @EnableDiscoveryClient @MapperScan("com.hy.demo.**.mapper") @EnableSwagger public class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext ctx = SpringApplication.run(DemoApplication.class, args); ctx.start(); } }
Configuration file plus swagger information:
swagger: enabled: true title: hy demo base-package: com.hy.demo
In this way, the business module part is completed, which is very simple and has zero code intrusion
Gateway development
The development of the gateway is an important play, which requires the integration of knife4j (this framework is the final version of the swagger bootstrap ui, the final version has changed its name, and the development is the same person), and the adaptation of the request url
pom. Adding the dependency of swagger to XML
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency>
In the gateway part of the configuration file, routes should be set separately for business interface configuration and swagger configuration, for example:
spring: application: name: gate cloud: gateway: discovery: locator: #enabled: true0 enabled: false lower-case-service-id: true routes: - id: demo uri: lb://demo predicates: - Path=/api/json/hy/demo/** - id: demoSwagger uri: lb://demo predicates: - Path=/demo/** filters: - SwaggerHeaderFilter - StripPrefix=1
We assume that the interface URL prefix definition of business forwarding is: / api/json/hy/demo. The demo is the name of the business module. The business interface does not need to add a filter for URL processing.
The suffix of route id is Swagger, so StripPrefix filter and custom filter SwaggerHeaderFilter need to be added.
Developed by ResourceConfig, this class is used to extract business module information from route information and display it in the drop-down box in the upper left corner of swagger ui
/** * Get the SwaggerResources list information, that is, the business module list * The list data is filled in the drop-down box in the upper left corner of the swagger ui * @description: * @author: demo * @create: 2020-02-25 17:15 **/ @Slf4j @Component @Primary @AllArgsConstructor public class SwaggerResourceConfig implements SwaggerResourcesProvider { private static final String SWAGGER_URI = "/v2/api-docs"; private final RouteLocator routeLocator; private final GatewayProperties gatewayProperties; @Override public List<SwaggerResource> get() { List<SwaggerResource> resources = new ArrayList<>(); List<String> routes = new ArrayList<>(); // Only the routing information with Swagger suffix is extracted routeLocator.getRoutes().filter(r -> r.getId().endsWith("Swagger")).subscribe(route -> routes.add(route.getId())); gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> { route.getPredicates().stream() .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName())) .forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(), predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0") .replace("/**", SWAGGER_URI)))); }); return resources; } private SwaggerResource swaggerResource(String name, String location) { log.info("name:{},location:{}",name,location); SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setName(name); swaggerResource.setLocation(location); swaggerResource.setSwaggerVersion("2.0"); return swaggerResource; } }
The example effect is as follows:
[figure 1]
Processing of swagger ui static resources:
/** * @description: * @author: demo * @create: 2020-02-25 17:19 **/ @RestController 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("/swagger-resources/configuration/security") public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK)); } @GetMapping("/swagger-resources/configuration/ui") public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK)); } @GetMapping("/swagger-resources") public Mono<ResponseEntity> swaggerResources() { return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); } }
swagger filter sample code
/** * @description: * @author: demo * @create: 2020-02-25 17:01 **/ @Component public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory { private static final String HEADER_NAME = "X-Forwarded-Prefix"; private static final String SWAGGER_URI = "/v2/api-docs"; @Override public GatewayFilter apply(Object config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); String path = request.getURI().getPath(); if (!StringUtils.endsWithIgnoreCase(path,SWAGGER_URI)) { return chain.filter(exchange); } String basePath = path.substring(0, path.lastIndexOf(SWAGGER_URI)); ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build(); ServerWebExchange newExchange = exchange.mutate().request(newRequest).build(); return chain.filter(newExchange); }; } }
Business filter modification
Since this component is a business gateway component, there must be a general filter to complete the functions of token verification and ID card identification. Remember to release the URL of "/ V2 / API docs" in these filters. Example code:
if (StringUtils.endsWithIgnoreCase(path,"/v2/api-docs")) { return chain.filter(exchange); }
Test verification
Start the corresponding registration center, business module and gateway module to verify whether the processing of swagger documents and business interfaces can be met at the same time.
Focus on Java high concurrency and distributed architecture, and share more technical dry goods and experiences. Please pay attention to official account: Java architecture community
You can scan the QR code on the left to add friends and invite you to join the wechat group of Java architecture community to discuss technology together