問題起因:使用Zuul網關服務,需要整合下游系統的swagger,但是下游服務存在context-path配置,無法正確跳轉,最后使用ZuulFilter解決。
1.Zuul整合下游swagger
首先介紹一下Zuul如何整合下游服務swagger,很好理解,就是通過Zuul的swagger地址,實現將下游服務的swagger都放入同一個頁面內,流轉圖如下:
1.1 下游服務整合swagger
這里進行簡單介紹服務整合swagger的步驟其實就是分為兩步:
- 配置swagger
- 對api和model等進行注釋
這里不做代碼介紹,具體可以查看我的另一篇文章:https://www.dalaoyang.cn/article/21,或者查看本文源碼。
這里只新建了一個服務,服務名為test-service。
1.2 Zuul聚合下游Swagger
Zuul相關配置這里不做介紹,首先配置下游服務路由,即訪問test-service/**轉發到test-service服務,配置如下:
zuul.routes.test-service.path=/test-service/**
zuul.routes.test-service.service-id=test-service
配置swagger配置文件,如下:
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("使用Swagger2構建RESTful APIs")
.description("關注博主博客:https://www.dalaoyang.cn/")
.termsOfServiceUrl("https://www.dalaoyang.cn/")
.contact("dalaoyang")
.version("1.0")
.build();
}
}
新建文檔配置,這里主要目的是為了聚合下游服務的swagger,內容很好理解,就是講SwaggerResource賦值,其中name為swagger的api文檔名,location為對應api-docs地址,version為版本,這里利用ZuulProperties來生成對應文檔,避免寫死代碼,完整內容如下:
@Primary
@Component
public class DocumentConfig implements SwaggerResourcesProvider {
@Autowired
private ZuulProperties zuulProperties;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> swaggerResources = new ArrayList<>();
Map<String, ZuulProperties.ZuulRoute> routes = zuulProperties.getRoutes();
for (String serviceName : routes.keySet()) {
SwaggerResource swaggerResource = initSwaggerResource(serviceName,
"/" + serviceName + "/v2/api-docs", "1.0.0");
swaggerResources.add(swaggerResource);
}
return swaggerResources;
}
private SwaggerResource initSwaggerResource(String name, String location, String version) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion(version);
return swaggerResource;
}
}
配置到這里,其實已經完成了,訪問網關swagger如圖所示:
2.下游服務存在context-path怎么辦?
從上面其實可以了解到,聚合文檔的操作,其實就是將下游服務的/v2/api-docs整合進來,當然,可以在本文DocumentConfig中將下游服務context-path加入其中,注意注釋階段,完整代碼如下:
@Primary
@Component
public class DocumentConfig implements SwaggerResourcesProvider {
@Autowired
private ZuulProperties zuulProperties;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> swaggerResources = new ArrayList<>();
Map<String, ZuulProperties.ZuulRoute> routes = zuulProperties.getRoutes();
for (String serviceName : routes.keySet()) {
//假設下游服務的context-path為服務名
SwaggerResource swaggerResource = initSwaggerResource(serviceName,
"/" + serviceName +"/" + serviceName + "/v2/api-docs", "1.0.0");
swaggerResources.add(swaggerResource);
}
return swaggerResources;
}
private SwaggerResource initSwaggerResource(String name, String location, String version) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion(version);
return swaggerResource;
}
}
下游服務加入context-path配置,如下:
server.servlet.context-path=/test-service
啟動服務,訪問Zuul的swagger文檔,還是可以同樣的訪問,但是測試一下在swagger請求一下下游服務api,如下
很明顯,404的原因就是因為轉發下游服務的時候,沒有加上context-path,在本文DocumentConfig配置的方式肯定不是正確的方式,那么如何解決呢?
可以加入一個ZuulFilter來進行統一添加下游服務context-path,首先還原上面修改的DocumentConfig,接下來新建一個Filter繼承ZuulFilter,創建一個轉發前的攔截器,將轉發地址進行修改,也就是我們需要的加入context-pa路徑,由於本文下游context-path路徑為服務名,所以案例比較簡單,內容如下:
@Component
public class UrlPathFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 6;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
Object requestURI = requestContext.get(FilterConstants.REQUEST_URI_KEY);
Object server = requestContext.get(FilterConstants.PROXY_KEY);
String finalURI = "/" + server + requestURI;
requestContext.put(FilterConstants.REQUEST_URI_KEY, finalURI);
return null;
}
}
需要注意一點,這個攔截器需要在默認ZuulFilter后執行,才能獲取requestURI和server。
再次啟動項目,就可以正常使用和訪問swagger了。
3.源碼地址
Zuul地址:https://gitee.com/dalaoyang/springcloud_learn/tree/master/springcloud_swagger_zuul
Test-service地址:https://gitee.com/dalaoyang/springcloud_learn/tree/master/springcloud_swagger_service