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

Spring Cloud Gateway修改响应体后内容被截断问题求助

解决Spring Cloud Gateway修改响应体后缓冲区截断的问题

嘿,这个问题我之前也碰到过!你已经成功修改了响应体,但忽略了一个关键步骤:更新响应头里的Content-Length字段,同时还要处理分块编码的情况。

原响应的Content-Length还是旧内容的长度,所以客户端(比如浏览器)会按照这个长度去读取新的响应体,自然就把超出部分截断了。下面是修改后的完整代码,我会给你标注关键修改点:

import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;

public class ModifyBodyGatewayFilterImpl implements GatewayFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();

        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                // 1. 收集原响应的所有数据(若无需原内容,可直接跳过并释放原缓冲区)
                return Flux.from(body)
                        .reduce((buffer1, buffer2) -> {
                            // 合并原响应的所有缓冲区
                            DataBuffer mergedBuffer = bufferFactory.allocateBuffer(
                                    buffer1.readableByteCount() + buffer2.readableByteCount());
                            mergedBuffer.write(buffer1);
                            mergedBuffer.write(buffer2);
                            // 释放原缓冲区,避免内存泄漏
                            DataBufferUtils.release(buffer1);
                            DataBufferUtils.release(buffer2);
                            return mergedBuffer;
                        })
                        .map(mergedBuffer -> {
                            // 2. 生成新的响应内容
                            String newResponseContent = "0123456789abcdefg";
                            byte[] newContentBytes = newResponseContent.getBytes(StandardCharsets.UTF_8);

                            // 3. 释放原合并的缓冲区
                            DataBufferUtils.release(mergedBuffer);

                            // 4. 分配对应大小的新缓冲区并写入内容
                            DataBuffer newBuffer = bufferFactory.allocateBuffer(newContentBytes.length);
                            newBuffer.write(newContentBytes);

                            // 5. 关键:更新响应头
                            HttpHeaders headers = getDelegate().getHeaders();
                            // 设置新的Content-Length为新内容的字节长度
                            headers.setContentLength(newContentBytes.length);
                            // 如果原响应使用了分块编码,移除这个头(因为现在是固定长度)
                            headers.remove(HttpHeaders.TRANSFER_ENCODING);

                            return newBuffer;
                        })
                        // 6. 将新缓冲区转为Flux,交给父类处理
                        .flatMap(newBuffer -> super.writeWith(Flux.just(newBuffer)));
            }
        };

        // 替换响应为装饰后的响应
        ServerWebExchange updatedExchange = exchange.mutate().response(decoratedResponse).build();
        return chain.filter(updatedExchange);
    }
}

关键修改点说明:

  • 更新Content-Length:这是解决截断问题的核心,让客户端明确知道新响应体的实际长度。
  • 释放原缓冲区:一定要记得释放原响应的缓冲区,避免内存泄漏。
  • 处理分块编码:如果原响应是分块传输(Transfer-Encoding: chunked),需要移除这个头,否则客户端可能会以分块方式解析固定长度的内容,导致异常。

如果不需要原响应的内容,还可以简化代码,直接跳过收集原数据的步骤,效率更高:

@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
    // 直接释放所有原缓冲区
    DataBufferUtils.release(body);
    
    String newResponseContent = "0123456789abcdefg";
    byte[] newContentBytes = newResponseContent.getBytes(StandardCharsets.UTF_8);
    
    // 更新响应头
    HttpHeaders headers = getDelegate().getHeaders();
    headers.setContentLength(newContentBytes.length);
    headers.remove(HttpHeaders.TRANSFER_ENCODING);
    
    DataBuffer newBuffer = bufferFactory.allocateBuffer(newContentBytes.length);
    newBuffer.write(newContentBytes);
    return super.writeWith(Flux.just(newBuffer));
}

这样修改后,新的响应体就不会被截断了,客户端会完整接收你设置的内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:43:27