SpringCloudGateway集成Swagger

1.背景#

微服务架构下,各个服务单独集成使用Swagger不方便,所以需要将其集中到网关使用

2.原理#

SwaggerResourcesProvider接口负责提供文档信息
官方默认提供实现类InMemorySwaggerResourcesProvider,只针对单服务场景
可以自定义实现类并配合网关或服务发现机制提供各服务的文档信息

3.实现#

自定义实现类核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Primary
@Component
public class CustomSwaggerResourcesProvider implements SwaggerResourcesProvider {

@Lazy
@Autowired
private RouteLocator routeLocator;

@Override
public List<SwaggerResource> get() {
List<Route> routes = new ArrayList<>();
routeLocator.getRoutes().subscribe(routes::add);
return routes.stream().map(route -> this.resource(route.getUri().getHost())).collect(Collectors.toList());
}

private SwaggerResource resource(String service) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(service);
swaggerResource.setUrl("/" + service + "/v3/api-docs");
swaggerResource.setSwaggerVersion("3.0.3");
return swaggerResource;
}
}

利用SpringCloudGateway的RouteLocator获取服务信息,组装并返回文档信息
@Primary注解使该自定义实现类代替默认的InMemorySwaggerResourcesProvider
网关只做此简单配置,Docket在各服务配置

4.补充#

问题:
如上配置后,在网关已能打开Swagger并出现各服务列表
但是发现服务接口调用不通,原因是网关截掉了路径中的服务信息,导致此处Swagger的Servers列表中的基地址缺少服务信息,所以无法路由到具体服务
而下游服务中的Swagger过滤器WebMvcBasePathAndHostnameTransformationFilter也未对此种情况进行支持(即便网关向下游服务提供了X-Forwarded-相关头部信息),所以提供给上游网关的Servers信息中才没有服务信息

解决方案:
在下游服务中自定义WebMvcOpenApiTransformationFilter实现类(可直接继承官方默认实现类WebMvcBasePathAndHostnameTransformationFilter),补充服务信息
确保优先级小于WebMvcBasePathAndHostnameTransformationFilter

自定义Filter核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
public class PrefixedWebMvcOpenApiTransformationFilter extends WebMvcBasePathAndHostnameTransformationFilter {

private static final String HEADER_X_FORWARDED_PREFIX = "X-Forwarded-Prefix";

public PrefixedWebMvcOpenApiTransformationFilter(String oasPath) {
super(oasPath);
}

@Override
public OpenAPI transform(OpenApiTransformationContext<HttpServletRequest> context) {
OpenAPI openApi = context.getSpecification();
context.request().ifPresent(servletRequest -> {
String headerValue = servletRequest.getHeader(HEADER_X_FORWARDED_PREFIX);
if (!StringUtils.isEmpty(headerValue)) {
openApi.getServers().forEach(server -> server.setUrl(server.getUrl() + headerValue));
}
});
return openApi;
}
}

配置该自定义Filter,核心代码如下:

1
2
3
4
5
6
7
8
9
@Configuration
public class SwaggerConfiguration {

@Bean
public WebMvcOpenApiTransformationFilter prefixedWebMvcOpenApiTransformer(
@Value(springfox.documentation.oas.web.SpecGeneration.OPEN_API_SPECIFICATION_PATH) String oasPath) {
return new PrefixedWebMvcOpenApiTransformationFilter(oasPath);
}
}

5.资料#

6.版本#

  • Spring Cloud Gateway 2.2.5
  • SpringFox(Swagger) 3.0.0