本文简单介绍在Spring Cloud Gateway项目里聚合多个微服务的swagger文档的实践经验。
在聚合各个微服务的api文档之前,您自然已经搭建好了微服务开发的框架,所以各个微服务项目里的服务注册与发现应该都已经配置好了。Swagger框架可以选用Springfox或Springdoc
原理简介
我们知道Swagger或着说后来的openApi是一种描述api接口文档的规范。每个使用了swagger的项目,在/v2/api-docs或/v3/api-docs路径下都可以看到该项目所有的接口描述。但是由于每个微服务的项目部署的ip地址和端口都不一样,所以需要分别访问每个项目的文档路径才能看到相应的地址,这是不方便的。现在我们使用了网关,而网关又有请求转发的功能,所以我们可以在网关的项目里,手动设置各个微服务swagger文档的资源路径,只是ip地址和端口都变成网关微服务的了。然后当我们向网关请求某个微服务api文档的路径时,网关会自动转发到对应的服务上。
在网关中配置好路由
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: 配置路由id
uri: lb://微服务名
order: -1
predicates:
-
filters:
-
Spring Cloud Gatway + Springfox
文档地址:http://springfox.github.io/springfox/docs/current/
第一步、在各项目里配置Springfox
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
使用Swaager2和Swagger3时,这两个地方设置不一样。
第二步、在网关中编写api文档资源路径
package cn.com.nbd.app.gateway.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
@Primary
public class GatewaySwaggerResourcesProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v3/api-docs";
@Value("${spring.application.name}")
private String gatewayApplicationName;
@Autowired
private RouteLocator routeLocator;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
routeLocator.getRoutes()
.filter(route -> route.getUri().getHost() != null
&& !gatewayApplicationName.equals(route.getUri().getHost())
&& !route.getId().startsWith("ReactiveCompositeDiscoveryClient")
)
.subscribe(route -> {
contextPath = route.getUri().getHost();
resources.add(swaggerResource(route.getId(),
"/" + contextPath + API_URI));
});
return resources;
}
private SwaggerResource swaggerResource(String name, String url) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setUrl(url);
swaggerResource.setSwaggerVersion("3.0");
return swaggerResource;
}
}
注意
- 截止2022/4/3,spring boot 2.6以上的版本使用springfox会出现问题
spring boot 2.6以上版本+spingfox 3.0会出现一个问题, Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerException 目前还没有解决办法,有成功解决的希望可以分享在评论里。 - swagger 3.0不需要为每个路由配置过滤器StripPrefix=1,否则会出现:Fetch errorNot Found
Spring Cloud Gatway + Springdoc
文档地址:https://springdoc.org/
第一步、引入依赖
在网关项目里引入:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-ui</artifactId>
<version>1.6.6</version>
</dependency>
在其他项目里:
如果是servlet:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webmvc-core</artifactId>
<version>1.6.6</version>
</dependency>
如果的webflux:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-core</artifactId>
<version>1.6.6</version>
</dependency>
第二步、手动配置api文档路径
@Configuration
public class SpringDocConfig {
@Bean
@Lazy(false)
public List<GroupedOpenApi> apis(SwaggerUiConfigParameters swaggerUiConfigParameters, RouteDefinitionLocator locator) {
List<GroupedOpenApi> groups = new ArrayList<>();
locator.getRouteDefinitions()
.filter(route -> route.getUri().getHost() != null
&& !gatewayApplicationName.equals(route.getUri().getHost())
&& !route.getId().startsWith("ReactiveCompositeDiscoveryClient")
)
.subscribe(routeDefinition -> {
contextPath = route.getUri().getHost();
swaggerUiConfigParameters.addGroup(name);
groups.add(GroupedOpenApi.builder()
.pathsToMatch("/" + contextPath + "/**")
.group(name)
.build());
});
return groups;
}
第三步、使用重写路径过滤器转换路径
springdoc中,我们手动设置的aip文档路径最后生成的格式为/v3/api-docs/{contextPath},所以我们需要把他转换成:/{contextPath}/v3/api-docs,可以借助网关中的路径重写过滤器。
- id: openapi
uri: http://localhost:${server.port}
order: -2 #该路由的优先级最后高于其他微服务的路由
predicates:
- Path=/v3/api-docs/**
filters:
- RewritePath=/v3/api-docs/(?<path>.*), /$\{path}/v3/api-docs
|