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
相关产品推荐
相关产品推荐

