如何从Spring的HttpMessageNotReadableException中获取原始请求体?
如何在HttpMessageNotReadableException中获取原始请求体
直接通过HttpMessageNotReadableException.getHttpInputMessage().getBody()无法拿到原始请求体,原因是Spring在解析请求体时已经将InputStream的字节读取完毕,且该流通常不支持mark/reset功能,无法重复读取。
要解决这个问题,需要提前缓存请求体,可通过自定义过滤器结合ContentCachingRequestWrapper实现:
1. 编写请求缓存过滤器
将原始请求包装为ContentCachingRequestWrapper,它会自动缓存请求体内容:
@Component public class RequestCachingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 仅针对带请求体的HTTP方法做缓存 if (HttpMethod.POST.name().equals(request.getMethod()) || HttpMethod.PUT.name().equals(request.getMethod())) { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); filterChain.doFilter(wrappedRequest, response); } else { filterChain.doFilter(request, response); } } }
2. 在异常处理器中读取缓存的请求体
在异常处理方法中注入HttpServletRequest,判断其是否为包装后的实例,再读取缓存内容:
@ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(HttpMessageNotReadableException.class) public GenericErrorResp exceptionHandler(HttpMessageNotReadableException e, HttpServletRequest request) { if (request instanceof ContentCachingRequestWrapper) { ContentCachingRequestWrapper wrappedRequest = (ContentCachingRequestWrapper) request; byte[] cachedBody = wrappedRequest.getContentAsByteArray(); if (cachedBody.length > 0) { String rawBody = new String(cachedBody, request.getCharacterEncoding()); log.error("Invalid request body: {}", rawBody, e); } else { log.error("Invalid body (empty or not cached)", e); } } else { log.error("Invalid body (request not wrapped)", e); } return new GenericErrorResp(); }
注意事项
ContentCachingRequestWrapper会将请求体缓存到内存中,对于超大请求体可能造成内存压力,建议根据业务场景限制请求体大小,或添加相应判断逻辑。- 过滤器需确保在Spring请求体解析前执行,使用
@Component注解的过滤器默认会在Spring MVC核心过滤器之前生效,无需额外配置。
内容的提问来源于stack exchange,提问作者Rubén Aguado
相关产品推荐
相关产品推荐

