在过去的两天里,我在请求到达客户端之前尝试了各种可能的方法来修改请求的响应正文,但似乎没有任何方法对我有用。到目前为止我已经尝试过提到的实现here https://gist.github.com/dalegaspi/03550807b1c84100dd028fc8e07950ff, here https://stackoverflow.com/questions/55574382/spring-cloud-gateway-for-composite-api-calls/71888244#71888244, here https://stackoverflow.com/questions/50101750/how-to-modify-the-response-body-in-spring-cloud-gateway-just-before-the-commit, here https://stackoverflow.com/questions/71465870/how-to-get-original-response-body-in-spring-cloud-gateway-webflux-post-filter, here https://gist.github.com/WeirdBob/b25569d461f0f54444d2c0eab51f3c48还有其他一些我现在找不到的,但没有任何效果。我是否将过滤器定义为前置、后置、全局、网关或特定于路由并不重要 - 实际的响应修改似乎对我不起作用。
我的情况如下:
我正在运行一个配置了 YAML 的 API 网关,并配置了其路由之一以在后台引导至 ADF 服务。我对该 ADF 应用程序遇到的问题是,它返回给客户端的响应采用由其后端自动生成的 HTML 模板的形式。在此模板中,某些 URL 是硬编码的并指向应用程序本身的地址。为了证明在这种情况下使用 API 网关的合理性,我想将这些 ADF URL 替换为 API 网关的 URL。
为简单起见,假设我的 ADF 服务的 IP 地址是1.2.3.4:1234
,我的API网关的IP地址是localhost:8080
。当我在网关中点击 ADF 路由时,响应包含一些自动生成的 JavaScript 插入内容,例如:
AdfPage.PAGE.__initializeSessionTimeoutTimer(1800000, 120000, "http://1.2.3.4:1234/entry/dynamic/index.jspx");
正如您所看到的,它包含一个硬编码的 URL。我想访问响应正文并找到所有这些硬编码的 URL 并将它们替换为网关 URL,因此上面的示例变为:
AdfPage.PAGE.__initializeSessionTimeoutTimer(1800000, 120000, "http://localhost:8080/entry/dynamic/index.jspx");
为此,我认为明智的做法是拥有一个全局 POST 过滤器,仅当请求与我的 ADF 应用程序的路由匹配时才会启动,所以这就是我决定要做的事情。
这是到目前为止我的后置过滤器:
@Bean
public GlobalFilter globalADFUrlReplacementFilter() {
return (exchange, chain) -> chain.filter(exchange).then(Mono.just(exchange)).map(serverWebExchange -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
if (requestIsTowardsADF(request)) {
logger.info("EXECUTING GLOBAL POST FILTER FOR ADF TEMPLATE URL REPLACEMENT");
ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(response) {
@Override
@SuppressWarnings("unchecked")
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
logger.info("OVERRIDING writeWith METHOD TO MODIFY THE BODY");
Flux<? extends DataBuffer> flux = (Flux<? extends DataBuffer>) body;
return super.writeWith(flux.buffer().map(buffer -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(buffer);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
DataBufferUtils.release(join);
String bodyStr = new String(content, StandardCharsets.UTF_8);
bodyStr = bodyStr.replace(ADF_URL, API_GATEWAY_URL);
getDelegate().getHeaders().setContentLength(bodyStr.getBytes().length);
return bufferFactory().wrap(bodyStr.getBytes());
}));
}
};
logger.info("ADF URL REPLACEMENT FILTER DONE");
return chain.filter(serverWebExchange.mutate().request(request).response(responseDecorator).build());
}
return serverWebExchange;
})
.then();
}
和配置:
spring:
cloud:
gateway:
routes:
- id: adf-test-2
uri: http://1.2.3.4:1234
predicates:
- Path=/entry/**
你可以看到我正在使用org.slf4j.Logger
对象在控制台中记录消息。当我运行 API Gateway 并点击 ADF 路由时,我可以看到以下内容:
EXECUTING GLOBAL POST FILTER FOR ADF TEMPLATE URL REPLACEMENT
ADF URL REPLACEMENT FILTER DONE
当我检查从 API 网关返回的响应时,我可以看到响应正文仍然相同,并且 ADF URL 根本没有被替换。我尝试调试应用程序,一旦它到达ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(response) {
它会跳过这些花括号内的整个匿名类实现。证明这一点的是缺乏OVERRIDING writeWith METHOD TO MODIFY THE BODY
登录控制台 - 它从未被执行!
似乎由于某种原因,实际的身体修改没有被执行,我不明白为什么。我尝试了此过滤器的几种不同实现,如上面的链接中所述,但它们都不起作用。
有人可以与我分享一个可以修改响应正文的工作 POST 过滤器,或者指出我的解决方案中的缺陷吗?
提前谢谢大家!