Gateway + OpenApi
爲了啓用 OpenApi,提供 API 的服務中需要添加以下依賴:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webmvc-core</artifactId>
<version>1.4.1</version>
</dependency>
我們的 gateway,Spring Cloud Gateway 使用 Netty 作爲內嵌服務器,Netty 基於 Spring WebFlux。同時,爲了顯示 UI 界面,也要提供相應的庫:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-core</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-ui</artifactId>
<version>1.4.1</version>
</dependency>
每個微服務都會暴露 /v3/api-docs
接口,用來獲取 API 信息。可以通過 springdoc.api-docs.path
配置來修改這個路由,建議不修改。不同於 Swagger,OpenAPI 並不提供類似 SwaggerResource
可以用來收集這些 API 信息的類
幸運的是,我們可以使用 SwaggerUiConfig
來實現我們的目的
@Component
public class OpenApiConfig {
public OpenApiConfig(RouteLocator locator, SwaggerUiConfig swaggerUiConfig) {
List<Route> routes = routeLocator.getRoutes();
// 去重
Collection<Route> distinctRoutes = routes.stream()
.collect(Collectors.toMap(Route::getLocation, p -> p, (p, q) -> p)).values();
// 將服務名添加到下拉框
distinctRoutes.stream().filter(route -> route.getLocation().matches(".+-service")).forEach(route -> {
String serviceName = route.getLocation();
swaggerUiConfig.addGroup(serviceName);
}
}
SwaggeUiConfig.addGroup(serviceName )
會在 UI 界面上生成一個下拉框,並加入一個名爲 serviceName 的選項。點擊這個選項將會訪問 /v3/api-docs/serviceName
,而我們期望的是 /serviceName/v3/api-docs
。所以,需要對路由進行重寫
在此之前,需要配置我們的路由
spring:
application:
name: gateway
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**
filters:
- RewritePath=/user/(?<path>.*), /$\{path}
- id: task-service
uri: lb://task-service
predicates:
- Path=/task/**
filters:
- RewritePath=/task/(?<path>.*), /$\{path}
uri: lb//user-service
將自動實現所有 user-service 實例間的負載均衡。 RewritePath=/service/(?<path>.*), /$\{path}
將在訪問請求時去掉 /service 前綴
然後,將 /v3/api-docs/serviceName
的路由重寫爲 /serviceName/v3/api-docs
- id: openapi
uri: http://localhost:${server.port}
predicates:
- Path=/v3/api-docs/**
filters:
- RewritePath=/v3/api-docs/(?<path>.*), /$\{path}/v3/api-docs
最後效果呈現如下:
Zuul + OpenApi
Zuul 內嵌的是 Tomcat,不需要 WebFlux 加持,所以使用以下依賴
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.4.1</version>
</dependency>
OpenApiConfig
和前半節中的相同
Zuul 中 路由配置:
spring:
application:
name: gateway
zuul:
ignoredPatterns:
- /v3/api-docs
- /v3/api-docs/swagger-config
routes:
user:
service-id: user-service
path: /user/**
task:
service-id: task-service
path: /task/**
apis:
service-id: gateway
path: /v3/api-docs/**
/v3/api-docs/swagger-config
並不需要路由,所以加到 ignoredPatterns
中
如我們看到的,Zuul 不提供 Gateway 中的 RewritePath。我們可以使用過濾器來完成重寫路由的工作:
@Component
public class UrlPathFilter extends ZuulFilter {
@Override
public String filterType() {
return "route";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
String requestURI = RequestContext.getCurrentContext().getRequest().getRequestURI();
return requestURI.matches("/v3/api-docs/.+");
}
@Override
public Object run() {
String requestURI = RequestContext.getCurrentContext().getRequest().getRequestURI();
// 提取出路由最後一節,即服務名
String servicePattern = "/v3/api-docs/(?<group>.+)";
Pattern compile = Pattern.compile(servicePattern);
Matcher matcher = compile.matcher(requestURI);
String group = "";
while (matcher.find()) {
group = matcher.group("group");
}
// 重寫路由
String path = "/" + group + "/v3/api-docs";
RequestContext context = RequestContext.getCurrentContext();
context.put(FilterConstants.REQUEST_URI_KEY, path);
System.out.println(path);
return null;
}
}
最後的效果和上面相同