SpringCloudGateway集成SpringDoc CORS问题
集成SpringDoc后,在gateway在线文档界面,请求具体的服务接口,报CORS问题
Failed to fetch.
Possible Reasons:
CORS
Network Failure
URL scheme must be “http” or “https” for CORS request.
分析
其实是网关直接请求具体服务/v3/api-docs接口(默认),获取文档数据,里面包含该服务注册上来的地址,gateway swagger-ui解析该接口数据,根据里面的地址直接请求。可是网关地址,跟具体的服务地址肯定不同源,在gateway集成界面请求,肯定报跨的问题。
或者是该微服务群,只能通过网关访问,直接请求具体的服务地址,网络不通。文章来源:https://www.toymoban.com/news/detail-693409.html
{
"openapi": "3.0.1",
"info": {
"title": "XX服务",
"description": "XX服务开发接口文档",
"version": "1.0.0"
},
"servers": [
{
"url": "http://[2408:8456:601:9f52:225d:8f68:5e14:6ff4]:8201",
"description": "Generated server url"
}
]
}
思路
通过在gateway编写全局GatewayFilter,拦截集成时请求的 /xx/v3/api-docs接口(默认)接口,修改返回的数据,在servers写入通过网关直接访问的地址,就能解决在线文档请求接口,存在的跨域问题和网络不通问题。文章来源地址https://www.toymoban.com/news/detail-693409.html
实现
/**
* 处理服务 /v3/api-docs接口返回的数据,在servers里添加可以通过网关直接访问的地址
*/
@Slf4j
@Component
@ConditionalOnProperty(name = SPRINGDOC_ENABLED, matchIfMissing = true)
public class DocServiceUrlModifyGatewayFilter implements GlobalFilter, Ordered {
@Autowired
private SpringDocConfigProperties springDocConfigProperties;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//直接用配置类的值,默认值是 /v3/api-docs
String apiPath = springDocConfigProperties.getApiDocs().getPath();
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
URI uri = request.getURI();
//非正常状态跳过
if (response.getStatusCode().value() != HttpStatus.OK.value()) {
return chain.filter(exchange);
}
//非springdoc文档不拦截
if (!(StringUtils.isNotBlank(uri.getPath()) && uri.getPath().endsWith(apiPath))) {
return chain.filter(exchange);
}
String uriString = uri.toString();
String gatewayUrl = uriString.substring(0, uriString.lastIndexOf(apiPath));
DataBufferFactory bufferFactory = response.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
//不处理
if (!(body instanceof Flux)) {
return super.writeWith(body);
}
//处理SpringDoc-OpenAPI
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffers);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
DataBufferUtils.release(join);
try {
// 流转为字符串
String responseData = new String(content, StandardCharsets.UTF_8);
Map<String, Object> map = JsonUtils.json2Object(responseData, Map.class);
//处理返回的数据
Object serversObject = map.get("servers");
if (null != serversObject) {
List<Map<String, Object>> servers = (List<Map<String, Object>>) serversObject;
Map<String, Object> gatewayServer = new HashMap<>();
//网关地址
gatewayServer.put("url", gatewayUrl);
gatewayServer.put("description", "Gateway server url");
//添加到第1个
servers.add(0, gatewayServer);
map.put("servers", servers);
responseData = JsonUtils.object2Json(map);
byte[] uppedContent = responseData.getBytes(StandardCharsets.UTF_8);
response.getHeaders().setContentLength(uppedContent.length);
return bufferFactory.wrap(uppedContent);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return bufferFactory.wrap(content);
}));
}
};
// replace response with decorator
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
@Override
public int getOrder() {
return -2;
}
class JsonUtils {
public static final ObjectMapper OBJECT_MAPPER = createObjectMapper();
/**
* 初始化ObjectMapper
*
* @return
*/
private static ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
// objectMapper.registerModule(new Hibernate4Module().enable(Hibernate4Module.Feature.FORCE_LAZY_LOADING));
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
public static String object2Json(Object o) {
StringWriter sw = new StringWriter();
JsonGenerator gen = null;
try {
gen = new JsonFactory().createGenerator(sw);
OBJECT_MAPPER.writeValue(gen, o);
} catch (IOException e) {
throw new RuntimeException("不能序列化对象为Json", e);
} finally {
if (null != gen) {
try {
gen.close();
} catch (IOException e) {
throw new RuntimeException("不能序列化对象为Json", e);
}
}
}
return sw.toString();
}
/**
* 将 json 字段串转换为 对象.
*
* @param json 字符串
* @param clazz 需要转换为的类
* @return
*/
public static <T> T json2Object(String json, Class<T> clazz) {
try {
return OBJECT_MAPPER.readValue(json, clazz);
} catch (IOException e) {
throw new RuntimeException("将 Json 转换为对象时异常,数据是:" + json, e);
}
}
}
}
到了这里,关于SpringCloudGateway集成SpringDoc CORS问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!