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

SpringBoot自定义过滤器读取请求体触发400错误求助

SpringBoot过滤器读请求体触发BAD Request的解决方法

问题根源

HttpServletRequest的输入流是一次性读取的,你在过滤器里调用getInputStream()读完请求体后,后续Spring MVC解析请求体(比如绑定到DTO)时,再尝试读取流就会发现流已经被耗尽,直接导致BAD Request错误。

解决方案:包装请求缓存请求体

需要自定义一个HttpServletRequestWrapper,把请求体缓存起来,让后续流程可以重复读取请求体,步骤如下:

1. 实现缓存请求体的RequestWrapper

public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
    private final byte[] cachedBody;

    public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
        super(request);
        // 读取原始请求体并缓存为字节数组
        InputStream requestInputStream = request.getInputStream();
        this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        // 返回可重复读取的缓存流
        return new ServletInputStream() {
            private final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cachedBody);

            @Override
            public boolean isFinished() {
                return byteArrayInputStream.available() == 0;
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener readListener) {}

            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    // 提供直接获取缓存请求体字符串的方法
    public String getCachedBody() {
        return new String(this.cachedBody, StandardCharsets.UTF_8);
    }
}

2. 修改过滤器代码,使用包装后的请求

public class PostURLFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        // 包装原始请求,缓存请求体
        CachedBodyHttpServletRequest cachedRequest = new CachedBodyHttpServletRequest(req);

        try {
            // 从包装后的请求读取缓存的请求体
            String body = cachedRequest.getCachedBody();
            System.out.println(body + " this is the body");
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("end");
        // 传递包装后的请求给后续处理链
        filterChain.doFilter(cachedRequest, servletResponse);
    }
}

3. 注意事项

  • 注册过滤器时,要确保这个过滤器在Spring MVC的请求解析过滤器之前执行。如果用@WebFilter注解,设置order = -1;如果用配置类注册FilterRegistrationBean,同样设置setOrder(-1)。
  • 原代码里的out.println应该是System.out.println(原代码未定义out变量,属于笔误),已修正。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 02:25:14