Spring WebFlux:大请求体场景下如何在WebFilter中获取请求体?
在WebFilter中正确获取大请求体的解决方案
嘿,我明白你现在遇到的问题了——直接用exchange.getRequest().getBody()拿到的请求体被截断了,这其实是Spring WebFlux的默认内存缓冲区限制在搞鬼,而且流式请求体只能被消费一次的特性也需要特别处理,下面给你一步步讲怎么解决:
1. 先调整请求体内存限制
Spring WebFlux默认限制了请求体的内存缓存大小(默认约256KB),如果你的请求体超过这个值,就会被截断。你需要在配置文件里增大这个限制:
在application.yml中配置:
spring: codec: max-in-memory-size: 10MB
或者在application.properties中:
spring.codec.max-in-memory-size=10MB
这个值可以根据你的实际请求体大小调整,比如设置成5MB、20MB都可以。
2. 正确读取并缓存请求体
因为WebFlux的请求体是Flux<DataBuffer>流式结构,一旦消费就无法再次读取,所以在WebFilter里读取请求体后,必须把它缓存起来,再包装成新的请求传递给后续的过滤器或处理器,避免后续流程拿不到请求体。
下面是完整的WebFilter示例代码:
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.stereotype.Component; 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; @Component public class FullRequestBodyFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { ServerHttpRequest originalRequest = exchange.getRequest(); // 合并所有DataBuffer成一个,完整读取请求体 return DataBufferUtils.join(originalRequest.getBody()) .flatMap(dataBuffer -> { // 将DataBuffer转换成字节数组,方便后续使用 byte[] bodyBytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bodyBytes); // 释放DataBuffer,避免内存泄漏 DataBufferUtils.release(dataBuffer); // 创建缓存了请求体的包装请求 ServerHttpRequest cachedRequest = new CachedBodyServerHttpRequest(originalRequest, bodyBytes); // 继续过滤器链,使用缓存后的请求 return chain.filter(exchange.mutate().request(cachedRequest).build()) // 异常时确保缓冲区被释放 .doOnError(error -> DataBufferUtils.release(dataBuffer)); }) // 处理请求体为空的情况,直接走原链 .switchIfEmpty(chain.filter(exchange)); } // 自定义请求包装类,用于缓存请求体 private static class CachedBodyServerHttpRequest extends ServerHttpRequestDecorator { private final byte[] cachedBody; public CachedBodyServerHttpRequest(ServerHttpRequest delegate, byte[] cachedBody) { super(delegate); this.cachedBody = cachedBody; } @Override public Flux<DataBuffer> getBody() { return Flux.defer(() -> { // 将缓存的字节数组重新包装成DataBuffer DataBuffer buffer = DefaultDataBufferFactory.sharedInstance.wrap(cachedBody); // 确保缓冲区在被丢弃时释放 return Flux.just(buffer).doOnDiscard(DataBuffer.class, DataBufferUtils::release); }); } } }
关键注意事项
- 内存泄漏预防:一定要记得释放
DataBuffer,WebFlux不会自动帮你释放,手动调用DataBufferUtils.release()或者通过doOnDiscard处理丢弃的情况,避免内存泄漏。 - 超大请求体处理:如果你的请求体特别大(比如超过几十MB),不建议全部缓存到内存里,这时候可以考虑流式处理或者配置磁盘缓存,但这种场景比较少见,大部分业务场景调整内存限制+缓存就足够了。
- 请求体重复读取:通过
ServerHttpRequestDecorator包装请求后,后续的Controller或者其他过滤器都可以正常调用getRequest().getBody()获取请求体,不会因为被消费过而失效。
内容的提问来源于stack exchange,提问作者xiaoyu
相关产品推荐
相关产品推荐

