正文
完美解决了这一问题。
核心包装器
请求包装器(
ContentCachingRequestWrapper
) :
缓存请求体字节数据,允许多次读取。典型场景:记录请求日志后,控制器仍能正常解析请求体。
响应包装器(
ContentCachingResponseWrapper
) :
缓存响应输出流,支持在响应提交前修改内容(如添加签名、动态拼接数据)。
实战应用(过滤器实现)
// 请求包装器过滤器
@Componentpublicclass RequestLogFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 包装请求,缓存输入流 ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); byte[] requestBody = wrappedRequest.getContentAsByteArray(); // 记录请求日志(可在此处添加自定义逻辑) log.debug("Received request body: {}", new String(requestBody)); // 传递包装后的请求,确保后续组件能重复读取 filterChain.doFilter(wrappedRequest, response); }}// 响应包装器过滤器@Componentpublicclass ResponseSignFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 包装响应,缓存输出流 ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response); // 执行后续处理(控制器逻辑) filterChain.doFilter(request, wrappedResponse); // 响应后处理:添加签名 byte[] responseBody = wrappedResponse.getContentAsByteArray(); String signature = generateSignature(responseBody); wrappedResponse.setHeader("X-Response-Signature", signature); // 必须调用此方法将缓存内容写入原始响应 wrappedResponse.copyBodyToResponse(); } private String generateSignature(byte[] body) { // 自定义签名逻辑 return Base64.getEncoder().encodeToString(body); }}
三、单次执行保障:OncePerRequestFilter 基类
在请求转发(
forward
)或包含(
include
)场景中,普通过滤器可能重复执行,导致逻辑混乱。
OncePerRequestFilter
确保过滤器在请求生命周期内仅执行一次,是处理状态性逻辑的理想选择。
核心优势
避免重复处理:
通过
shouldNotFilter
方法内部逻辑,自动识别同一请求的多次调度,确保
doFilterInternal
仅执行一次。
简化开发:
只需重写
doFilterInternal
方法,无需手动处理请求标识或缓存执行状态。
适用场景
日志记录:
避免转发时重复打印日志。
安全校验: