ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
## :-: 微服务聚合Swagger文档 >[success] 微服务项目中,没有做API文档聚合,访问每个服务的API文档都需要访问单独的\`swagger-ui.html\`页面,既然我们使用了微服务,就应该有统一的API文档入口,\`knife4j\`有这方面的支持,本文将详细介绍其实现,希望对大家有所帮助! > 采用Nacos作为注册中心,Gateway作为网关,使用`knife4j`来生成API文档。 相关服务划分: * hjmall-gateway:网关服务,作为微服务API文档的访问入口,聚合所有API文档,需要引入文档前端UI包; * hjmall-accout:用户服务,普通API服务,不需要引入文档前端UI包; * hjmall-market:营销服务,普通API服务,不需要引入文档前端UI包。 ## 具体实现 >[danger] 下面详细介绍下Spring Cloud Gateway + knife4j 聚合API文档的具体实现,依次搭建用户服务、营销服务和网关服务 每个服务引入如下jar包 ~~~ <!--swagger2:API 文档生成工具 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> ~~~ * hjmall-accout >[danger] 我们首先来搭建用户服务,一个普通的API服务,很简单,仅需三步即可集成knife4j。 * 在`pom.xml`中添加相关依赖,一个SpringBoot的web功能依赖,knife4j的微服务依赖(不包含API文档的前端UI包); ~~~ <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-micro-spring-boot-starter</artifactId> </dependency> ~~~ >[danger] 添加Swagger相关配置,非常常规的配置,添加`@EnableKnife4j`注解开启knife4j的增强功能。 ~~~ Swagger2Config 配置如下 ~~~ ~~~ package com.hjf.market.core; import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 @EnableKnife4j public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.hjf.market.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("营销系统接口Api") .description("营销系统接口Api") .termsOfServiceUrl("") .contact("devteam") .version("1.0") .build(); } } ~~~ >[danger] 我们接下来搭建营销服务,一个普通的API服务,直接参考上面用户服务的搭建即可。 >[danger] 最后我们搭建网关服务,作为微服务API文档的的统一入口, > 聚合所有微服务的API文档。在`pom.xml`中添加相关依赖, > Gateway相关依赖和knife4j的Starter(包含API文档的前端UI包); ~~~ <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> </dependency> ~~~ >[danger] 在网关上添加Swagger资源配置,用于聚合其他微服务中Swagger的`api-docs`访问路径; > SwaggerResourceConfig 配置如下 ~~~ package com.hjf.gateway.swagger; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.config.GatewayProperties; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.support.NameUtils; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import springfox.documentation.swagger.web.SwaggerResource; import springfox.documentation.swagger.web.SwaggerResourcesProvider; import java.util.ArrayList; import java.util.List; /** * Swagger资源配置 */ @Slf4j @Component @Primary @AllArgsConstructor public class SwaggerResourceConfig implements SwaggerResourcesProvider { private final RouteLocator routeLocator; private final GatewayProperties gatewayProperties; @Override public List<SwaggerResource> get() { List<SwaggerResource> resources = new ArrayList<>(); List<String> routes = new ArrayList<>(); routeLocator.getRoutes().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("**", "v2/api-docs")))); }); 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; } } ~~~ 什么是Swagger的`api-docs`访问路径?该路径会返回JSON格式数据,Swagger渲染API文档页面的所有数据就是来源于此,比如我们的用户服务会返回如下信息,访问地址:[http://localhost:9000/api/account/v2/api-docs](http://localhost:9000/api/account/v2/api-docs) ![](https://img.kancloud.cn/1c/44/1c443af00848dce15dbf28a56d47e307_775x430.png) >[danger] 接下来我们需要自定义Swagger各个配置的节点,简单来说就是自定义Swagger内部的各个获取数据的接口; > SwaggerHandler配置 ~~~ package com.hjf.gateway.swagger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; import springfox.documentation.swagger.web.SecurityConfigurationBuilder; import springfox.documentation.swagger.web.SwaggerResourcesProvider; import springfox.documentation.swagger.web.UiConfiguration; import springfox.documentation.swagger.web.UiConfigurationBuilder; import springfox.documentation.swagger.web.*; import java.util.Optional; @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-resources`这个接口,可用于获取所有微服务的`api-docs`访问路径,获取信息如下,访问地址:[http://localhost:9000](http://localhost:9000)[/swagger-resources](http://localhost:9201/swagger-resources) ![](https://img.kancloud.cn/ff/bc/ffbcdb0f3f8bb8b679b9af7e6ff89080_743x479.png) ## 功能演示 >[danger] 接下来我们来演示下微服务API文档聚合的功能,仅需要访问网关的API文档页面即可,可自行切换到相关服务的API文档。 > 在此之前先启动我们的Nacos注册中心,然后依次启动`hjmall-gateway、hjmall-accout、hjmall-market`服务; > 访问 localhost:9000/doc.html ![](https://img.kancloud.cn/53/73/5373c11c8de9d4f758b40320a72fdaca_708x420.png) ## 总结 >[danger] 对比knife4j和原生Swagger的微服务使用,再次证明knife4j是springfox-swagger的增强UI实现,完全遵循了springfox-swagger中的使用方式。 > 官方文档:[https://doc.xiaominfo.com/guide/ui-front-gateway.html](https://doc.xiaominfo.com/guide/ui-front-gateway.html)