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

Spring拦截器preHandle中ContentCachingRequestWrapper请求体为空排查

问题根因

ContentCachingRequestWrapper 没有主动预加载请求体的逻辑,内部缓存是被动填充的:只有当请求链路中其他组件(比如Controller层的@RequestBody参数解析器、业务代码主动调用getInputStream()/getReader()方法)读取过请求体后,它才会把读取到的内容存入内部的字节缓存数组。

不同拦截节点的表现差异原因:

  • preHandle 是Controller方法执行前的拦截节点,此时请求体还未被任何下游组件读取,ContentCachingRequestWrapper 内部缓存为空,因此调用getContentAsByteArray()只能拿到空内容。
  • afterCompletion 是整个请求处理全链路完成后才执行的节点,此时Controller已经完成请求体的解析读取,缓存已经被填充,因此可以正常获取到完整请求内容。
修复方法

如果需要在preHandle阶段就获取请求体内容,需要在自定义过滤器中包装完请求后,主动触发一次请求体读取,提前完成缓存填充,修改后的过滤器代码如下:

@Component
public class CustomServletWrappingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        ContentCachingRequestWrapper wrappingRequest = new ContentCachingRequestWrapper(httpServletRequest);
        ContentCachingResponseWrapper wrappingResponse = new ContentCachingResponseWrapper(httpServletResponse);
        // 主动读取请求体,提前写入缓存,不会影响后续组件重复读取
        wrappingRequest.getInputStream().readAllBytes();
        filterChain.doFilter(wrappingRequest, wrappingResponse);
        wrappingResponse.copyBodyToResponse();
    }
}

注意:Java 8及以下版本没有readAllBytes()方法,可以手动通过字节数组循环读取流内容完成缓存初始化。该操作不会影响后续链路对请求体的读取,ContentCachingRequestWrapper 会把首次读取的内容全部存入缓存,后续所有对请求输入流的读取操作都会直接从缓存中取数据,不会出现流被耗尽无法重复读取的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 16:45:45