发布于2026-07-01 阅读(0)
扫一扫,手机访问
做过过滤器的朋友都知道,想在过滤器中拿到请求参数,这事儿其实有点讲究。URL上拼接的参数倒是好说,直接调一下request.getParameterMap()就能搞定。但body体里的内容就没这么简单了——request里提供了一个getInputStream()方法,问题在于,这个流是一次性的。如果你在过滤器中把它读出来了,那到了控制层那边,就什么都拿不到了。

看一个典型错误做法:在过滤器中直接调用原始请求的 getInputStream() 或 getReader() 来打印body。表面上看没问题,但流一旦被读取就会被消耗,等传到控制器那边,数据早就没了。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 直接读取原始流并打印(错误!)
String body = IOUtils.toString(httpRequest.getInputStream(), StandardCharsets.UTF_8);
System.out.println("Filter打印body:" + body);
// 此时流已被消耗,控制器无法读取
chain.doFilter(request, response);
}
问题的根源其实很清楚:输入流的“一次性”特性。
ServletRequest 提供的输入流(getInputStream() / getReader())是单向流动的字节流,默认情况下,一旦你读了(比如在过滤器中打印),流的指针就会移动到末尾。等后面控制器再来读,能拿到的自然就是空数据了。
所以核心不在于“是否在过滤器中打印”,而在于“打印时有没有消耗原始流,并且没有做缓存”。
正确的思路是这样的:先通过 HttpServletRequestWrapper 把流数据缓存起来,然后在过滤器中读取缓存的数据来打印,这样原始流就不会被消耗,控制器那边也能通过包装类的缓存正常读取。
过滤器内容
@Component
@0rder(o)
@Slf4j
public class WebFilter extends OncePerRequestFilter {
@0verride
protected void dofilterInternal(HttpservletRequest request, HttpservletResponse response, Filterchain filterchain)throws servletException, I0Exception{
// 正确示例:基于包装类的缓存打印,不消耗原始流
if (request instanceof HttpServletRequest) {
// 先包装请求,缓存body
CachedServletRequestWrapper wrappedRequest = new CachedServletRequestWrapper((HttpServletRequest) request);
// 从包装类的缓存中读取并打印(不影响原始流)
String body = new String(wrappedRequest.getCachedBody(), StandardCharsets.UTF_8);
System.out.println("Filter打印body:" + body);
// 传递包装后的请求,控制器可从缓存中读取
chain.doFilter(wrappedRequest, response);
} else {
chain.doFilter(request, response);
}
}
CachedServletRequestWrapper.ja va
import ja vax.servlet.ReadListener;
import ja vax.servlet.ServletInputStream;
import ja vax.servlet.http.HttpServletRequest;
import ja vax.servlet.http.HttpServletRequestWrapper;
import ja va.io.ByteArrayInputStream;
import ja va.io.ByteArrayOutputStream;
import ja va.io.IOException;
import ja va.io.InputStream;
import ja va.nio.charset.StandardCharsets;
/**
* 缓存请求体的包装类,解决ServletRequest输入流只能读取一次的问题
*/
public class CachedServletRequestWrapper extends HttpServletRequestWrapper {
// 缓存的请求体字节数组
private final byte[] cachedBody;
/**
* 构造方法:读取原始请求的输入流并缓存
*/
public CachedServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
this.cachedBody = readRequestBody(request);
}
/**
* 读取原始请求的输入流并转换为字节数组
*/
private byte[] readRequestBody(HttpServletRequest request) throws IOException {
// GET/DELETE等方法通常没有请求体,直接返回空数组
String method = request.getMethod();
if ("GET".equalsIgnoreCase(method) || "DELETE".equalsIgnoreCase(method)) {
return new byte[0];
}
// 读取输入流并缓存
try (InputStream inputStream = request.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
return outputStream.toByteArray();
}
}
/**
* 重写getInputStream(),返回基于缓存的输入流
*/
@Override
public ServletInputStream getInputStream() throws IOException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cachedBody);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return byteArrayInputStream.a vailable() == 0;
}
@Override
public boolean isReady() {
return true; // 同步读取时始终就绪
}
@Override
public void setReadListener(ReadListener listener) {
// 异步场景可忽略,同步场景无需实现
}
};
}
/**
* 重写getReader(),基于缓存的输入流构建字符读取器
*/
@Override
public ja va.io.BufferedReader getReader() throws IOException {
return new ja va.io.BufferedReader(
new ja va.io.InputStreamReader(getInputStream(), StandardCharsets.UTF_8)
);
}
/**
* 提供获取缓存的请求体字节数组的方法(方便过滤器/拦截器直接使用)
*/
public byte[] getCachedBody() {
return cachedBody;
}
/**
* 提供获取缓存的请求体字符串的方法(简化使用)
*/
public String getCachedBodyAsString() {
return new String(cachedBody, StandardCharsets.UTF_8);
}
}
从运行结果来看,body数据已经被正常打印了。

售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8