ContentCachingRequestWrapper 和 ContentCachingResponseWrapper
这两个类是 Spring Web 里非常实用但也很容易踩坑的工具,常见于:
👉 日志记录 / 请求响应体打印 / 审计 / 签名校验
🧠 一、核心一句话
👉 ContentCachingRequestWrapper 和 ContentCachingResponseWrapper 的本质:
把原本“只能读一次”的请求/响应流缓存起来,让你可以“重复读取”。
⚠️ 二、为什么需要它?
在 Servlet 规范中:
1️⃣ Request Body(请求体)
request.getInputStream()
👉 只能读一次!
读完就没了:
Filter 读了 → Controller 就读不到了 ❌
2️⃣ Response Body(响应体)
response.getOutputStream()
👉 也是写出去就没法再拿回来
✅ 三、ContentCachingRequestWrapper(请求缓存)
📌 作用
👉 缓存请求体(body),支持后续重复读取
📌 使用方式(典型 Filter)
ContentCachingRequestWrapper requestWrapper =
new ContentCachingRequestWrapper(request);
filterChain.doFilter(requestWrapper, response);
// 在请求处理完成后读取
byte[] buf = requestWrapper.getContentAsByteArray();
String body = new String(buf, request.getCharacterEncoding());
📌 关键点(面试重点)
⚠️ 必须“先执行链,再读取”
filterChain.doFilter(...)
👉 否则缓存里是空的!
原因:
👉 它是“被动缓存”——只有在真正读取 InputStream 时才会缓存
📌 常见用途
- 打印请求日志
- 接口签名验证
- 防重放攻击
✅ 四、ContentCachingResponseWrapper(响应缓存)
📌 作用
👉 缓存响应内容(body)
📌 使用方式
ContentCachingResponseWrapper responseWrapper =
new ContentCachingResponseWrapper(response);
filterChain.doFilter(request, responseWrapper);
// 获取响应内容
byte[] buf = responseWrapper.getContentAsByteArray();
String body = new String(buf, response.getCharacterEncoding());
// ⚠️ 关键:必须手动写回响应
responseWrapper.copyBodyToResponse();
⚠️ 关键坑(高频面试点)
❗ 必须调用:
responseWrapper.copyBodyToResponse();
否则:
👉 客户端收不到响应!!
🧩 五、完整 Filter 示例(生产可用)
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
ContentCachingRequestWrapper req =
new ContentCachingRequestWrapper((HttpServletRequest) request);
ContentCachingResponseWrapper resp =
new ContentCachingResponseWrapper((HttpServletResponse) response);
chain.doFilter(req, resp);
String requestBody = new String(
req.getContentAsByteArray(),
request.getCharacterEncoding()
);
String responseBody = new String(
resp.getContentAsByteArray(),
response.getCharacterEncoding()
);
System.out.println("REQ: " + requestBody);
System.out.println("RESP: " + responseBody);
// 必须写回
resp.copyBodyToResponse();
}
}
🔥 六、底层原理(面试加分)
1️⃣ RequestWrapper
InputStream → 读取 → 同时写入 byte[] cache
👉 类似“旁路复制”
2️⃣ ResponseWrapper
写 OutputStream → 先写到缓存 → 最后再写回真正 response
⚠️ 七、常见坑总结(一定要会)
❌ 1:读取不到 request body
原因:
还没被消费(Controller 还没读)
❌ 2:response 丢失
原因:
没调用 copyBodyToResponse()
❌ 3:大文件内存爆炸
👉 因为:
全部缓存到内存(byte[])
👉 解决:
- 限制大小
- 不对大文件启用
❌ 4:只对 body 生效
👉 不能缓存:
- query 参数
- header
🚀 八、面试总结(建议背)
👉 ContentCachingRequestWrapper 和 ResponseWrapper 本质是通过包装 ServletRequest/Response,将流数据缓存到内存中,从而解决请求和响应流只能读/写一次的问题,常用于日志记录和审计场景,但需要注意请求是延迟缓存、响应必须手动写回,以及大数据量带来的内存问题。

浙公网安备 33010602011771号