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

Spring Boot Webflux中如何完整克隆含Body的ServerResponse?

解决方案:在Spring Boot 2.0.1 Functional Endpoint中克隆ServerResponse并添加自定义头

针对你在Spring Boot 2.0.1.RELEASE + Functional Endpoint场景下,想通过过滤器给所有响应加自定义头但无法复制响应体的问题,我有几个实用的解决方案:

方案1:使用ExchangeFilterFunction(推荐)

ExchangeFilterFunction是WebFlux Functional模式下专门用于请求/响应拦截的组件,我们可以通过它拦截响应,读取原始响应体后重新构建带自定义头的响应。

实现自定义过滤器

import org.springframework.web.reactive.function.server.ExchangeFilterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

public class CustomHeaderFilter {
    public static ExchangeFilterFunction addCustomResponseHeader() {
        // 拦截响应处理逻辑
        return ExchangeFilterFunction.ofResponseProcessor(originalResponse -> {
            // 读取原始响应体(这里以String为例,若为JSON对象可替换为对应DTO类型)
            return originalResponse.bodyToMono(String.class)
                    .flatMap(responseBody -> {
                        // 构建新的ServerResponse:复制原始状态码、响应头,添加自定义头,写入原始响应体
                        return ServerResponse.status(originalResponse.statusCode())
                                .headers(headers -> {
                                    headers.addAll(originalResponse.headers().asHttpHeaders());
                                    headers.add("X-Custom-Header", "Your-Custom-Value");
                                })
                                .bodyValue(responseBody);
                    });
        });
    }
}

在路由中注册过滤器

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
public class ApiRouterConfig {

    @Bean
    public RouterFunction<ServerResponse> apiRoutes(YourRequestHandler handler) {
        return RouterFunctions.route()
                // 配置你的API路由
                .GET("/api/resources", handler::getAllResources)
                .POST("/api/resources", handler::createResource)
                // 注册自定义过滤器
                .filter(CustomHeaderFilter.addCustomResponseHeader())
                .build();
    }
}

方案2:处理大文件/二进制响应

如果你的接口返回大文件或二进制数据,使用bodyToMono(String.class)可能会导致内存溢出,这时候可以用DataBuffer来流式处理:

import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.web.reactive.function.server.ExchangeFilterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class CustomHeaderFilter {
    public static ExchangeFilterFunction addCustomResponseHeaderForBinary() {
        return ExchangeFilterFunction.ofResponseProcessor(originalResponse -> {
            // 以Flux形式读取原始响应体的DataBuffer
            return originalResponse.bodyToFlux(DataBuffer.class)
                    .collectList()
                    .flatMap(dataBuffers -> {
                        // 合并所有DataBuffer(避免多次IO)
                        DataBuffer combinedBuffer = originalResponse.exchange().bufferFactory().join(dataBuffers);
                        // 保留DataBuffer引用,防止被提前释放导致内存泄漏
                        DataBufferUtils.retain(combinedBuffer);
                        // 构建新响应
                        return ServerResponse.status(originalResponse.statusCode())
                                .headers(headers -> {
                                    headers.addAll(originalResponse.headers().asHttpHeaders());
                                    headers.add("X-Custom-Header", "Binary-Response-Value");
                                })
                                .bodyValue(Flux.just(combinedBuffer));
                    });
        });
    }
}

注意事项

  • Spring Boot 2.0.1对应Spring WebFlux 5.0.x,上述API均兼容该版本;
  • 如果你的响应体是特定类型(如自定义DTO),只需将bodyToMono(String.class)替换为bodyToMono(YourDto.class)即可;
  • 处理二进制响应时,务必通过DataBufferUtils.retain()保留缓冲区引用,避免出现IllegalStateException。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:10:20