聚合微服务中的 Swagger API 文档

Posted by 暮夏有五 on 2021-02-14
Estimated Reading Time 8 Minutes
Words 1.9k In Total
Viewed Times

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

我们将采用 Nacos 作为注册中心,Gateway 作为网关,使用 knife4j 来生成 API 文档。

应用架构


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

相关服务划分:

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

具体实现


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

创建 knife4j-user 项目模块

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

在 pom.xml 中添加相关依赖,一个 Spring Boot 的 web 功能依赖,knife4j 的微服务依赖(不包含 API 文档的前端 UI 包):

1
2
3
4
5
6
7
8
9
10
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
</dependency>
</dependencies>

application.yml 中添加相关配置,配置一下 Nacos 注册中心即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:
port: 9501
spring:
profiles:
active: dev
application:
name: knife4j-user
cloud:
nacos:
discovery:
server-addr: localhost:8848
management:
endpoints:
web:
exposure:
include: "*"

添加 Swagger 相关配置,非常常规的配置,添加 @EnableKnife4j 注解开启 knife4j 的增强功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
@EnableSwagger2
@EnableKnife4j
public class Swagger2Config {
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.macro.cloud.controller"))
.paths(PathSelectors.any())
.build();
}

private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("knife4j-user")
.description("用户服务API文档")
.contact("antonio")
.version("1.0")
.build();
}
}

创建 knife4j-order 项目模块

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

创建 knife4j-gateway 项目模块

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

在 pom.xml 中添加相关依赖,Gateway 相关依赖和 knife4 j的 Starter(包含 API 文档的前端 UI 包):

1
2
3
4
5
6
7
8
9
10
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
</dependencies>

application.yml 这添加相关配置,配置一下 Nacos 注册中心,用户服务和订单服务的路由即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
server:
port: 9201
spring:
profiles:
active: dev
application:
name: knife4j-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes: #配置路由路径
- id: user-service
uri: lb://knife4j-user
predicates:
- Path=/user-service/**
filters:
- StripPrefix=1
- id: order-service
uri: lb://knife4j-order
predicates:
- Path=/order-service/**
filters:
- StripPrefix=1
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能
lower-case-service-id: true #使用小写服务名,默认是大写

在网关上添加 Swagger 资源配置,用于聚合其他微服务中 Swagger 的 api-docs 访问路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@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<>();
//获取所有路由的ID
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
//过滤出配置文件中定义的路由->过滤出Path Route Predicate->根据路径拼接成api-docs路径->生成SwaggerResource
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:9201/user-service/v2/api-docs

img

接下来我们需要自定义 Swagger 各个配置的节点,简单来说就是自定义 Swagger 内部的各个获取数据的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* 自定义Swagger的各个配置节点
*/
@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;
}

/**
* Swagger安全配置,支持oauth和apiKey设置
*/
@GetMapping("/swagger-resources/configuration/security")
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}

/**
* Swagger UI配置
*/
@GetMapping("/swagger-resources/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}

/**
* Swagger资源配置,微服务中这各个服务的api-docs信息
*/
@GetMapping("/swagger-resources")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}

比如说 swagger-resources 这个接口,可用于获取所有微服务的 api-docs 访问路径,获取信息如下,访问地址:http://localhost:9201/swagger-resources

img

功能演示


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

在此之前先启动我们的 Nacos 注册中心,然后依次启动 knife4j-userknife4j-orderknife4j-gateway 服务:

img

从网关访问 API 文档,访问地址:http://localhost:9201/doc.html

img

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

img

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

img

切换回 Swagger UI


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

首先我们需要在 pom.xml 中去除 knife4j 的相关依赖,主要就是下面两个依赖:

1
2
3
4
5
6
7
8
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>

pom.xml 中添加 Swagger 相关依赖,并去除原来使用的 @EnableKnife4j 注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<dependencies>
<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>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>

重新启动所有服务,访问网关的 API 文档路径即可查看:http://localhost:9201/swagger-ui.html

img

更多干货请移步:https://antoniopeng.com


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !