Zuul整合Swagger,使用ZuulFilter解決下游服務context-path
問題起因:使用Zuul閘道器服務,需要整合下游系統的swagger,但是下游服務存在context-path配置,無法正確跳轉,最後使用ZuulFilter解決。
1.Zuul整合下游swagger
首先介紹一下Zuul如何整合下游服務swagger,很好理解,就是通過Zuul的swagger地址,實現將下游服務的swagger都放入同一個頁面內,流轉圖如下:
1.1 下游服務整合swagger
這裡進行簡單介紹服務整合swagger的步驟其實就是分為兩步:
- 配置swagger
- 對api和model等進行註釋
這裡不做程式碼介紹,具體可以檢視我的另一篇文章: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",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地址:gitee.com/dalaoyang/s…
Test-service地址:gitee.com/dalaoyang/s…