自研網(wǎng)關(guān)系統(tǒng)已開(kāi)源,求star
項(xiàng)目地址
- 網(wǎng)關(guān)地址:https://gitee.com/starmark947618/starmark-gateway
網(wǎng)關(guān)一般都會(huì)有一個(gè)swagger聚合功能,方便于你測(cè)試文檔接口。
但網(wǎng)上的例子,如https://doc.xiaominfo.com/solution/ui-front-gateway.html#%E6%96%87%E6%A1%A3%E8%81%9A%E5%90%88%E4%B8%9A%E5%8A%A1%E7%BC%96%E7%A0%81.
這些例子都能解決swagger聚合的問(wèn)題,但問(wèn)題是他只支持一個(gè)項(xiàng)目,但我網(wǎng)關(guān)肯定會(huì)有多個(gè)項(xiàng)目,這個(gè)怎么處理呢。
其實(shí)這個(gè)原理一樣,但說(shuō)起來(lái)也是有幾個(gè)技術(shù)難點(diǎn)的.
- doc.html路徑問(wèn)題:
swagger默認(rèn)的文檔路徑為doc.html或swagger-ui.html,但如果是多項(xiàng)目,即我這個(gè)項(xiàng)目,路徑需要變成/項(xiàng)目編碼/doc.html,這個(gè)怎么解決如下,如解決不當(dāng),會(huì)報(bào)404或跨域問(wèn)題
@Component
public class SwaggerFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
//獲取工程編碼
String projectCode= RequestHelper.getRequestProjectCode(exchange.getRequest());
if (StringUtils.isNotEmpty(projectCode)&&projectCode.endsWith(GatewayConstant.STARMARK_SWAGGER)) {
if (exchange.getRequest().getURI().getPath().contains(projectCode + "/doc.html")) {
return chain.filter(exchange.mutate().request(exchange.getRequest().mutate().path("/doc.html").build()).build());
} else if (exchange.getRequest().getURI().getPath().contains(projectCode + "/webjars/")) {
return chain.filter(exchange.mutate().request(exchange.getRequest().mutate().path(exchange.getRequest().getURI().getPath().replace("/" + projectCode, "")).build()).build());
}
}
return chain.filter(exchange);
}
}
通過(guò)過(guò)濾器,將請(qǐng)求轉(zhuǎn)向根請(qǐng)求
2.支持多項(xiàng)目的配置,每個(gè)請(qǐng)求都含項(xiàng)目編碼,知道屬于哪個(gè)項(xiàng)目.
@Controller
@RequestMapping()
public class SwaggerHandler {
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
@Autowired(required = false)
private UiConfiguration uiConfiguration;
private final SwaggerProvider swaggerResources;
@Autowired
public SwaggerHandler(SwaggerProvider swaggerResources) {
this.swaggerResources = swaggerResources;
}
@GetMapping("/{projectCode}/swagger-resources/configuration/security")
@ResponseBody
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration(@PathVariable("projectCode") String projectCode) {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/{projectCode}/swagger-resources/configuration/ui")
@ResponseBody
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration(){
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/{projectCode}/swagger-resources")
@ResponseBody
public Mono<ResponseEntity> swaggerResources(@PathVariable("projectCode") String projectCode) {
return Mono.just((new ResponseEntity<>(swaggerResources.get(projectCode), HttpStatus.OK)));
}
}
@Component
@Primary
@AllArgsConstructor
public class SwaggerProvider {
@Autowired
private IRouteService routeService;
public List<SwaggerResource> get(String projectCode) {
//獲取真正的projectCode
String innerProjectCode = projectCode.replace(GatewayConstant.STARMARK_SWAGGER, "");
List<GatewayAppDto> appDtos = routeService.getGatewayApps(innerProjectCode);
List<SwaggerResource> resources = appDtos.stream().map(this::swaggerResource).collect(Collectors.toList());
return resources;
}
private SwaggerResource swaggerResource(GatewayAppDto gatewayAppVo) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(gatewayAppVo.getAppName());
swaggerResource.setLocation("/v2/api-docs?swagger_code="+gatewayAppVo.getAppCode());
swaggerResource.setSwaggerVersion("2.0");
return swaggerResource;
}
}
這樣,我返回的每個(gè)接口地址都含有一個(gè)子模塊參數(shù)swagger_code
3.解決swagger_code參數(shù)帶來(lái)的不顯示文檔的問(wèn)題
現(xiàn)在其實(shí)已經(jīng)能顯示swagger聚合文檔,但發(fā)現(xiàn)顯示不全,文檔接口全沒(méi)有了。
經(jīng)排查,是接口/v2/api-docs不允許有任何參數(shù)。
解決辦法時(shí),網(wǎng)關(guān)轉(zhuǎn)發(fā)時(shí),使用RemoveRequestParameter把該參數(shù)swagger_code去掉
示例如下:
private List<FilterDefinition> createSwaggerFilterDefinition() {
List<FilterDefinition> filterDefinitions=new ArrayList<>();
{
Map<String, String> args = new HashMap<>();
args.put("name", "swagger_code");
FilterDefinition filterDefinition = new FilterDefinition();
filterDefinition.setName("RemoveRequestParameter");
filterDefinition.setArgs(args);
filterDefinitions.add(filterDefinition);
}
filterDefinitions.add(createFilterDefinition("1"));
return filterDefinitions;
}
至此,多項(xiàng)目的網(wǎng)關(guān)聚合功能已完成.

具體代碼大家可以去我的開(kāi)源項(xiàng)目中看,歡迎提ISSUE及需求。
因?yàn)橹挥幸粋€(gè)人,也歡迎大家參與進(jìn)來(lái).
- 網(wǎng)關(guān)地址:https://gitee.com/starmark947618/starmark-gateway
下期需求計(jì)劃:
網(wǎng)關(guān)配置轉(zhuǎn)發(fā)后,可能有些接口出問(wèn)題,臨時(shí)不允許訪問(wèn),或升級(jí)時(shí)轉(zhuǎn)發(fā)到另一地址,需要做url的下架,轉(zhuǎn)發(fā)功能