You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

关键说明

  1. 过滤器类型选择:处理响应必须用ModifyResponseBodyGatewayFilterFactory,而不是处理请求的ModifyRequestBodyGatewayFilterFactory。
  2. 响应头时机:必须在chain.filter(exchange).then(...)的回调中处理,此时后端服务已经返回响应,响应头才会被填充。
  3. 压缩处理:保持和后端一致的压缩格式,避免前端无法解析响应内容。

内容的提问来源于stack exchange,提问作者zy_sun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 17:57:51