Spring Gateway如何获取源响应头并替换代理HTML内容?
Spring Cloud Gateway 响应体修改与源响应头获取方案
问题原因
你当前继承的ModifyRequestBodyGatewayFilterFactory是用于修改请求体的过滤器,此时还未发起对后端服务的调用,自然无法获取到后端返回的Content-Type响应头,这就是值为null的原因。要处理响应体,应该继承ModifyResponseBodyGatewayFilterFactory,它专门用于拦截和修改后端返回的响应内容。
Spring Gateway 获取源响应头的方式
在Spring Cloud Gateway中,后端服务的响应头可以通过ServerWebExchange对象获取,当进入响应处理阶段时,exchange.getResponse().getHeaders()会包含后端返回的所有响应头(包括Content-Type)。
完整实现示例
以下是对应Zuul逻辑的Spring Gateway自定义过滤器实现,包含Content-Type判断、gzip解压缩/重新压缩、响应体修改的完整流程:
@Component @Slf4j public class ReplaceTextGatewayFilterFactory extends ModifyResponseBodyGatewayFilterFactory { @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { return chain.filter(exchange).then(Mono.defer(() -> { // 获取后端响应头 HttpHeaders responseHeaders = exchange.getResponse().getHeaders(); List<String> contentTypeList = responseHeaders.get(HttpHeaders.CONTENT_TYPE); boolean isHtml = false; if (contentTypeList != null) { isHtml = contentTypeList.stream() .anyMatch(type -> type.contains("text/html")); } if (!isHtml) { // 非HTML内容,直接返回原始响应 return Mono.empty(); } // 获取原始响应体 ServerHttpResponse originalResponse = exchange.getResponse(); DataBufferFactory bufferFactory = originalResponse.bufferFactory(); return exchange.getResponse().getBody() .map(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); // 处理gzip解压缩 byte[] decompressedBytes = bytes; List<String> contentEncoding = responseHeaders.get(HttpHeaders.CONTENT_ENCODING); if (contentEncoding != null && contentEncoding.contains("gzip")) { try (GZIPInputStream gzipIn = new GZIPInputStream(new ByteArrayInputStream(bytes))) { decompressedBytes = StreamUtils.copyToByteArray(gzipIn); } catch (IOException e) { log.error("Gzip decompression failed", e); } } // 将字节转为字符串并修改内容 String body = new String(decompressedBytes, StandardCharsets.UTF_8); String modifiedBody = modifyBody(exchange.getRequest(), body); // 重新压缩为gzip(保持原始编码格式) byte[] finalBytes = modifiedBody.getBytes(StandardCharsets.UTF_8); if (contentEncoding != null && contentEncoding.contains("gzip")) { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); GZIPOutputStream gzipOut = new GZIPOutputStream(bos)) { gzipOut.write(finalBytes); gzipOut.finish(); finalBytes = bos.toByteArray(); } catch (IOException e) { log.error("Gzip compression failed", e); } } // 替换响应体 DataBuffer buffer = bufferFactory.wrap(finalBytes); originalResponse.getHeaders().setContentLength(buffer.readableByteCount()); originalResponse.getBody().writeWith(Mono.just(buffer)); return buffer; }); })); }; } // 自定义的响应体修改逻辑,对应原Zuul的modifyBody2方法 private String modifyBody(ServerHttpRequest request, String body) { // 这里替换成你的文本修改逻辑,比如替换特定字符串 return body.replace("旧文本", "新文本"); } // 过滤器配置类(如果不需要自定义配置,保持空即可) public static class Config {} }
路由配置调整
确保你的路由配置中使用正确的过滤器名称(注意Spring Gateway过滤器名称默认是类名去掉GatewayFilterFactory后缀,首字母小写):
spring: cloud: gateway: routes: - id: replace_route uri: http://my-web-site-article/ predicates: - Path=/article/** filters: - ReplaceText
关键说明
- 过滤器类型选择:处理响应必须用
ModifyResponseBodyGatewayFilterFactory,而不是处理请求的ModifyRequestBodyGatewayFilterFactory。 - 响应头时机:必须在
chain.filter(exchange).then(...)的回调中处理,此时后端服务已经返回响应,响应头才会被填充。 - 压缩处理:保持和后端一致的压缩格式,避免前端无法解析响应内容。
内容的提问来源于stack exchange,提问作者zy_sun
相关产品推荐
相关产品推荐

