摘要

记得我的mall-swarm微服务项目中,没有做API文档聚合,访问每个服务的API文档都需要访问单独的swagger-ui.html页面,既然我们使用了微服务,就应该有统一的API文档入口,最近发现knife4j有这方面的支持,本文将详细介绍其实现,希望对大家有所帮助!

前置知识

我们将采用Nacos作为注册中心,Gateway作为网关,使用knife4j来生成API文档,对这些技术不了解的朋友可以看下下面的文章。

  • Spring Cloud Gateway:新一代API网关服务
  • Spring Cloud Alibaba:Nacos 作为注册中心和配置中心使用
  • 给Swagger换了个新皮肤,瞬间高大上了!

应用架构

我们理想的解决方案应该是这样的,网关作为API文档的统一入口,网关聚合所有微服务的文档,通过在网关进行切换来实现对其他服务API文档的访问。

相关服务划分:

  • micro-knife4j-gateway:网关服务,作为微服务API文档的访问入口,聚合所有API文档,需要引入文档前端UI包;
  • micro-knife4j-user:用户服务,普通API服务,不需要引入文档前端UI包;
  • micro-knife4j-order:订单服务,普通API服务,不需要引入文档前端UI包。

具体实现

下面详细介绍下Spring Cloud Gateway + knife4j 聚合API文档的具体实现,依次搭建用户服务、订单服务和网关服务。

micro-knife4j-user

我们首先来搭建用户服务,一个普通的API服务,很简单,仅需三步即可集成knife4j。

  • 在pom.xml中添加相关依赖,一个SpringBoot的web功能依赖,knife4j的微服务依赖(不包含API文档的前端UI包);
  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.github.xiaoymin</groupId>
  8. <artifactId>knife4j-micro-spring-boot-starter</artifactId>
  9. </dependency>
  10. </dependencies>
  11. 复制代码
  • 在application.yml这添加相关配置,配置一下Nacos注册中心即可;
  1. server:
  2. port: 9501
  3. spring:
  4. profiles:
  5. active: dev
  6. application:
  7. name: micro-knife4j-user
  8. cloud:
  9. nacos:
  10. discovery:
  11. server-addr: localhost:8848
  12. management:
  13. endpoints:
  14. web:
  15. exposure:
  16. include: "*"
  17. 复制代码
  • 添加Swagger相关配置,非常常规的配置,添加@EnableKnife4j注解开启knife4j的增强功能。
  1. /**
  2. * Swagger API相关配置
  3. */
  4. @Configuration
  5. @EnableSwagger2
  6. @EnableKnife4j
  7. public class Swagger2Config {
  8. @Bean
  9. public Docket createRestApi(){
  10. return new Docket(DocumentationType.SWAGGER_2)
  11. .apiInfo(apiInfo())
  12. .select()
  13. .apis(RequestHandlerSelectors.basePackage("com.macro.cloud.controller"))
  14. .paths(PathSelectors.any())
  15. .build();
  16. }
  17. private ApiInfo apiInfo() {
  18. return new ApiInfoBuilder()
  19. .title("micro-knife4j-user")
  20. .description("用户服务API文档")
  21. .contact("macro")
  22. .version("1.0")
  23. .build();
  24. }
  25. }
  26. 复制代码

micro-knife4j-order

我们接下来搭建订单服务,一个普通的API服务,直接参考上面用户服务的搭建即可。

micro-knife4j-gateway

最后我们搭建网关服务,作为微服务API文档的的统一入口,聚合所有微服务的API文档。

  • 在pom.xml中添加相关依赖,Gateway相关依赖和knife4j的Starter(包含API文档的前端UI包);
  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-gateway</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.github.xiaoymin</groupId>
  8. <artifactId>knife4j-spring-boot-starter</artifactId>
  9. </dependency>
  10. </dependencies>
  • 在application.yml这添加相关配置,配置一下Nacos注册中心,用户服务和订单服务的路由即可;
  1. server:
  2. port: 9201
  3. spring:
  4. profiles:
  5. active: dev
  6. application:
  7. name: micro-knife4j-gateway
  8. cloud:
  9. nacos:
  10. discovery:
  11. server-addr: localhost:8848
  12. gateway:
  13. routes: #配置路由路径
  14. - id: user-service
  15. uri: lb://micro-knife4j-user
  16. predicates:
  17. - Path=/user-service/**
  18. filters:
  19. - StripPrefix=1
  20. - id: order-service
  21. uri: lb://micro-knife4j-order
  22. predicates:
  23. - Path=/order-service/**
  24. filters:
  25. - StripPrefix=1
  26. discovery:
  27. locator:
  28. enabled: true #开启从注册中心动态创建路由的功能
  29. lower-case-service-id: true #使用小写服务名,默认是大写
  • 在网关上添加Swagger资源配置,用于聚合其他微服务中Swagger的api-docs访问路径;
  1. /**
  2. * Swagger资源配置
  3. * Created by macro on 2020/7/9.
  4. */
  5. @Slf4j
  6. @Component
  7. @Primary
  8. @AllArgsConstructor
  9. public class SwaggerResourceConfig implements SwaggerResourcesProvider {
  10. private final RouteLocator routeLocator;
  11. private final GatewayProperties gatewayProperties;
  12. @Override
  13. public List<SwaggerResource> get() {
  14. List<SwaggerResource> resources = new ArrayList<>();
  15. List<String> routes = new ArrayList<>();
  16. //获取所有路由的ID
  17. routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
  18. //过滤出配置文件中定义的路由->过滤出Path Route Predicate->根据路径拼接成api-docs路径->生成SwaggerResource
  19. gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).forEach(route -> {
  20. route.getPredicates().stream()
  21. .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
  22. .forEach(predicateDefinition -> resources.add(swaggerResource(route.getId(),
  23. predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
  24. .replace("**", "v2/api-docs"))));
  25. });
  26. return resources;
  27. }
  28. private SwaggerResource swaggerResource(String name, String location) {
  29. log.info("name:{},location:{}", name, location);
  30. SwaggerResource swaggerResource = new SwaggerResource();
  31. swaggerResource.setName(name);
  32. swaggerResource.setLocation(location);
  33. swaggerResource.setSwaggerVersion("2.0");
  34. return swaggerResource;
  35. }
  36. }
  • 什么是Swagger的api-docs访问路径?该路径会返回JSON格式数据,Swagger渲染API文档页面的所有数据就是来源于此,比如我们的用户服务会返回如下信息,访问地址:http://localhost:9201/user-service/v2/api-docs

微服务聚合Swagger文档,这波操作是真的香! - 图1

微服务聚合Swagger文档,这波操作是真的香! - 图2

  • 接下来我们需要自定义Swagger各个配置的节点,简单来说就是自定义Swagger内部的各个获取数据的接口;
  1. /**
  2. * 自定义Swagger的各个配置节点
  3. * Created by macro on 2020/7/9.
  4. */
  5. @RestController
  6. public class SwaggerHandler {
  7. @Autowired(required = false)
  8. private SecurityConfiguration securityConfiguration;
  9. @Autowired(required = false)
  10. private UiConfiguration uiConfiguration;
  11. private final SwaggerResourcesProvider swaggerResources;
  12. @Autowired
  13. public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
  14. this.swaggerResources = swaggerResources;
  15. }
  16. /**
  17. * Swagger安全配置,支持oauth和apiKey设置
  18. */
  19. @GetMapping("/swagger-resources/configuration/security")
  20. public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
  21. return Mono.just(new ResponseEntity<>(
  22. Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
  23. }
  24. /**
  25. * Swagger UI配置
  26. */
  27. @GetMapping("/swagger-resources/configuration/ui")
  28. public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
  29. return Mono.just(new ResponseEntity<>(
  30. Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
  31. }
  32. /**
  33. * Swagger资源配置,微服务中这各个服务的api-docs信息
  34. */
  35. @GetMapping("/swagger-resources")
  36. public Mono<ResponseEntity> swaggerResources() {
  37. return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
  38. }
  39. }

微服务聚合Swagger文档,这波操作是真的香! - 图3

微服务聚合Swagger文档,这波操作是真的香! - 图4

功能演示

接下来我们来演示下微服务API文档聚合的功能,仅需要访问网关的API文档页面即可,可自行切换到相关服务的API文档。

  • 在此之前先启动我们的Nacos注册中心,然后依次启动micro-knife4j-user、micro-knife4j-order及micro-knife4j-gateway服务;

微服务聚合Swagger文档,这波操作是真的香! - 图5

微服务聚合Swagger文档,这波操作是真的香! - 图6

微服务聚合Swagger文档,这波操作是真的香! - 图7

微服务聚合Swagger文档,这波操作是真的香! - 图8

  • 我们通过左上角的切换组件即可切换到对应服务的API文档;

微服务聚合Swagger文档,这波操作是真的香! - 图9

微服务聚合Swagger文档,这波操作是真的香! - 图10

  • 查看API文档,我们可以发现所有接口都已经添加了对应的访问前缀,可以正常访问。

微服务聚合Swagger文档,这波操作是真的香! - 图11

微服务聚合Swagger文档,这波操作是真的香! - 图12

切换回Swagger UI

如果你不想使用knife4j的界面,想用原来的Swagger界面,也是可以支持的,切换方法非常简单,下面我们来讲讲。

  • 首先我们需要在pom.xml中去除knife4j的相关依赖,主要就是下面两个依赖;
  1. <dependencies>
  2. <dependency>
  3. <groupId>com.github.xiaoymin</groupId>
  4. <artifactId>knife4j-micro-spring-boot-starter</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.github.xiaoymin</groupId>
  8. <artifactId>knife4j-spring-boot-starter</artifactId>
  9. </dependency>
  10. </dependencies>
  11. 复制代码
  • 在pom.xml中添加Swagger相关依赖,并去除原来使用的@EnableKnife4j注解;
  1. <dependencies>
  2. <dependency>
  3. <groupId>io.springfox</groupId>
  4. <artifactId>springfox-swagger2</artifactId>
  5. <version>2.9.2</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>io.springfox</groupId>
  9. <artifactId>springfox-swagger-ui</artifactId>
  10. <version>2.9.2</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>io.swagger</groupId>
  14. <artifactId>swagger-models</artifactId>
  15. <version>1.6.0</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>io.swagger</groupId>
  19. <artifactId>swagger-annotations</artifactId>
  20. <version>1.6.0</version>
  21. </dependency>
  22. </dependencies>
  23. 复制代码

微服务聚合Swagger文档,这波操作是真的香! - 图13

微服务聚合Swagger文档,这波操作是真的香! - 图14

总结

对比knife4j和原生Swagger的微服务使用,再次证明knife4j是springfox-swagger的增强UI实现,完全遵循了springfox-swagger中的使用方式。