Knife4j在Gateway下的URI优化以及热刷新
契机
(遗留输出)最近在整理之前的笔记,逐渐梳理成文章输出到博客网站。之前在做Gateway集成knife4j的时候。发现uri的地址缺少了项目路径,也就是baseURI,本篇文章就是在处理这个问题。还有gateway一旦启动会固化服务列表,加了一个小改动动态刷新服务列表,这样不用每次在最后重启gateway。
添加BaseURI
java
@Slf4j
@Component
public class SwaggerGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().toString();
if (!path.endsWith("/v3/api-docs")) {
return chain.filter(exchange);
}
String[] pathArray = path.split("/");
String basePath = pathArray[1];
ServerHttpResponse originalResponse = exchange.getResponse();
// 定义新的消息头
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public @NotNull Mono<Void> writeWith(@NotNull Publisher<? extends DataBuffer> body) {
if (Objects.equals(super.getStatusCode(), HttpStatus.OK) && body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = Flux.from(body);
return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
List<String> list = new ArrayList<>();
dataBuffers.forEach(dataBuffer -> {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
DataBufferUtils.release(dataBuffer);
list.add(new String(content, StandardCharsets.UTF_8));
});
String s = this.listToString(list);
JSONObject jsonObject = JSON.parseObject(s);
//给所有url加上basePath前缀
JSONObject test = new JSONObject();
for (String paths : jsonObject.getJSONObject("paths").keySet()) {
test.put("/" + basePath + paths, jsonObject.getJSONObject("paths").getJSONObject(paths));
}
jsonObject.remove("paths");
jsonObject.put("paths", test);
s = jsonObject.toString();
int length = s.getBytes().length;
HttpHeaders headers = originalResponse.getHeaders();
headers.setContentLength(length);
return bufferFactory().wrap(s.getBytes(StandardCharsets.UTF_8));
}));
}
return super.writeWith(body);
}
@Override
public @NotNull HttpHeaders getHeaders() {
// 获取父类原始ServerHttpResponse的header请求头信息,这是代理Delegate类型
HttpHeaders httpHeaders = super.getHeaders();
httpHeaders.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
return httpHeaders;
}
private String listToString(List<String> list) {
StringBuilder stringBuilder = new StringBuilder();
for (String s : list) {
stringBuilder.append(s);
}
return stringBuilder.toString();
}
};
// replace response with decorator
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
@Override
public int getOrder() {
return -2;
}
}
动态刷新服务列表
java
@Configuration(proxyBeanMethods = false)
@Slf4j
public class SpringDocConfiguration {
@Bean
@Lazy(false)
@ConditionalOnProperty(name = "springdoc.api-docs.enabled", matchIfMissing = true)
public List<GroupedOpenApi> apis(SwaggerUiConfigParameters swaggerUiConfigParameters,
SwaggerDocProperties swaggerProperties) {
List<GroupedOpenApi> groups = new ArrayList<>();
for (String value : swaggerProperties.getServices().values()) {
swaggerUiConfigParameters.addGroup(value);
}
return groups;
}
@Data
@Component
@ConfigurationProperties("swagger")
public class SwaggerDocProperties implements InitializingBean {
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
Set<AbstractSwaggerUiConfigProperties.SwaggerUrl> urlsBackup = new HashSet<>();
@Override
public void afterPropertiesSet() {
scheduledExecutorService.scheduleWithFixedDelay(new DelayTaskHandler(), 3, 3, TimeUnit.SECONDS);
}
public class DelayTaskHandler implements Runnable {
@SneakyThrows
@Override
public void run() {
try {
Set<String> ignoreServices = new HashSet<>();
//遍历
for (String s : services.keySet()) {
NacosDiscoveryClient bean = SpringUtil.getBean(NacosDiscoveryClient.class);
if (!bean.getServices().contains(s)) {
ignoreServices.add(services.get(s));
}
}
SwaggerUiConfigParameters swaggerUiConfigParameters = SpringUtil.getBean(
SwaggerUiConfigParameters.class);
Set<AbstractSwaggerUiConfigProperties.SwaggerUrl> urls = swaggerUiConfigParameters.getUrls();
if (urlsBackup.size() == 0) {
urlsBackup.addAll(urls);
}
urls.clear();
for (AbstractSwaggerUiConfigProperties.SwaggerUrl url : urlsBackup) {
if (!ignoreServices.contains(url.getName())) {
urls.add(url);
}
}
swaggerUiConfigParameters.setUrls(urls);
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
private Map<String, String> services;
/**
* 认证参数
*/
private SwaggerBasic basic = new SwaggerBasic();
@Data
public class SwaggerBasic {
/**
* 是否开启 basic 认证
*/
private Boolean enabled;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
}
}
}
总结
- 有点水
- 纯记录,后续都使用apifox了