SpringCloudGateway集成SpringDoc CORS問題
集成SpringDoc后,在gateway在線文檔界面,請求具體的服務(wù)接口,報CORS問題
Failed to fetch.
Possible Reasons:
CORS
Network Failure
URL scheme must be “http” or “https” for CORS request.
分析
其實是網(wǎng)關(guān)直接請求具體服務(wù)/v3/api-docs接口(默認),獲取文檔數(shù)據(jù),里面包含該服務(wù)注冊上來的地址,gateway swagger-ui解析該接口數(shù)據(jù),根據(jù)里面的地址直接請求??墒蔷W(wǎng)關(guān)地址,跟具體的服務(wù)地址肯定不同源,在gateway集成界面請求,肯定報跨的問題。
或者是該微服務(wù)群,只能通過網(wǎng)關(guān)訪問,直接請求具體的服務(wù)地址,網(wǎng)絡(luò)不通。文章來源:http://www.zghlxwxcb.cn/news/detail-693409.html
{
"openapi": "3.0.1",
"info": {
"title": "XX服務(wù)",
"description": "XX服務(wù)開發(fā)接口文檔",
"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接口(默認)接口,修改返回的數(shù)據(jù),在servers寫入通過網(wǎng)關(guān)直接訪問的地址,就能解決在線文檔請求接口,存在的跨域問題和網(wǎng)絡(luò)不通問題。文章來源地址http://www.zghlxwxcb.cn/news/detail-693409.html
實現(xiàn)
/**
* 處理服務(wù) /v3/api-docs接口返回的數(shù)據(jù),在servers里添加可以通過網(wǎng)關(guān)直接訪問的地址
*/
@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();
//非正常狀態(tài)跳過
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 {
// 流轉(zhuǎn)為字符串
String responseData = new String(content, StandardCharsets.UTF_8);
Map<String, Object> map = JsonUtils.json2Object(responseData, Map.class);
//處理返回的數(shù)據(jù)
Object serversObject = map.get("servers");
if (null != serversObject) {
List<Map<String, Object>> servers = (List<Map<String, Object>>) serversObject;
Map<String, Object> gatewayServer = new HashMap<>();
//網(wǎng)關(guān)地址
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 字段串轉(zhuǎn)換為 對象.
*
* @param json 字符串
* @param clazz 需要轉(zhuǎn)換為的類
* @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 轉(zhuǎn)換為對象時異常,數(shù)據(jù)是:" + json, e);
}
}
}
}
到了這里,關(guān)于SpringCloudGateway集成SpringDoc CORS問題的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!