使用ContentCachingRequestWrapper缓存请求后,如何读取控制器请求体?
为什么用ContentCachingRequestWrapper包装后,@RequestBody还能正常读取请求体?
这是个很常见的疑问,核心在于你对ContentCachingRequestWrapper的工作流程和Spring MVC的请求处理逻辑有点误解,我来给你拆解清楚:
1. 过滤器并没有提前消耗请求流
你在过滤器里做的只是把原始的HttpServletRequest包装成ContentCachingRequestWrapper,这个过程并没有调用getReader()或getInputStream()方法——也就是说,请求流此时完全没有被读取过,自然不存在“流已被消耗”的问题。真正触发请求体读取的操作,是Spring MVC在处理@RequestBody注解参数的时候。
2. ContentCachingRequestWrapper的核心是“缓存”而非“提前读取”
这个Wrapper的设计目的,就是解决Servlet请求流只能读取一次的痛点。它的工作逻辑是:
- 当第一次调用
getReader()/getInputStream()时,它会读取原始请求流的内容,同时把内容缓存到内部的字节数组中 - 之后任何再次调用这两个方法的请求,都会直接从缓存的字节数组中返回数据,而不会再去读取原始流
这样一来,无论是Spring MVC处理@RequestBody,还是你后续在异常处理中要记录请求体,都能正常获取到请求内容。
3. 验证:在异常处理中读取缓存的请求体
比如你可以在全局异常处理器中这样获取并记录请求体:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(YourCustomException.class) public ResponseEntity<ErrorResponse> handleCustomException(HttpServletRequest request) throws IOException { // 把request强转为ContentCachingRequestWrapper ContentCachingRequestWrapper cachedRequest = (ContentCachingRequestWrapper) request; // 从缓存中获取请求体字节数组,转成字符串 String requestBody = new String(cachedRequest.getContentAsByteArray(), cachedRequest.getCharacterEncoding()); // 这里可以把requestBody写入日志 log.error("自定义异常发生,请求体:{}", requestBody); return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new ErrorResponse("处理失败", requestBody)); } }
简单总结:你的过滤器只是给请求加了个“缓存buff”,并没有提前消耗流;Spring MVC处理@RequestBody时会触发第一次读取并缓存,后续的任何读取操作都走缓存,所以两者完全不冲突~
内容的提问来源于stack exchange,提问作者Ankit Arora
相关产品推荐
相关产品推荐

