SpringBoot项目中GlobalExceptionHandler如何正确获取RequestBody参数
问题根因
你遇到的null问题核心原因有两个:
- 异常发生在
@RequestBody参数解析完成、进入Controller方法体之前,你写在Controller里的setAttribute逻辑还没有执行 - HttpServletRequest的输入流默认只能读取一次,Spring参数解析阶段已经把流读完了,就算你直接在异常处理类里读流也会拿到空内容
解决方案
方案1:缓存请求体(通用方案,适配所有异常场景)
通过过滤器把原始请求包装成可重复读取的ContentCachingRequestWrapper,缓存请求体内容,异常阶段直接从缓存读取。
- 实现请求缓存过滤器
import org.springframework.web.util.ContentCachingRequestWrapper; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; // 可以调整urlPatterns只拦截需要的接口路径 @WebFilter(urlPatterns = "/*") public class CachingRequestBodyFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (request instanceof HttpServletRequest) { // 排除文件上传类请求避免内存占用过高 String contentType = request.getContentType(); if (contentType == null || !contentType.startsWith("multipart/")) { ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper((HttpServletRequest) request); chain.doFilter(wrappedRequest, response); return; } } chain.doFilter(request, response); } }
- 启动类添加
@ServletComponentScan注解,扫描刚才定义的过滤器 - 改造异常处理类读取缓存请求体
@ControllerAdvice public class GlobalErrorHandler{ @Autowired private ObjectMapper objectMapper; @ExceptionHandler({RuntimeException.class}) public ResponseEntity<ErrorDetails> generateRuntimeException(RuntimeException re, HttpServletRequest servletRequest) throws IOException { ErrorDetails errorDetails = new ErrorDetails(); Product product = null; // 先尝试拿Controller里存的属性,避免重复反序列化 Object attribute = servletRequest.getAttribute("product"); if (attribute != null) { product = (Product) attribute; } else if (servletRequest instanceof ContentCachingRequestWrapper) { ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) servletRequest; byte[] content = wrapper.getContentAsByteArray(); if (content.length > 0 && wrapper.getContentType() != null && wrapper.getContentType().contains("application/json")) { // 如果需要适配多接口,可以根据请求路径判断反序列化的实体类类型 product = objectMapper.readValue(content, Product.class); } } // 后续业务逻辑 errorDetails.setTimestamp(LocalDateTime.now()); errorDetails.setMessage("500"); errorDetails.setDetails(re.getMessage()); log.error("Exception occured: ", re); return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR); } }
方案2:参数校验/解析异常专用方案
如果你的异常大多是@RequestBody校验失败、类型转换失败抛出的MethodArgumentNotValidException、HttpMessageNotReadableException,可以直接从异常对象中获取已经解析完成的参数:
@ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorDetails> handleValidException(MethodArgumentNotValidException e) { // 直接拿到已经解析完成的Product对象 Product product = (Product) e.getBindingResult().getTarget(); // 后续业务逻辑 }
方案3:自定义参数解析器提前存入属性
如果希望所有解析成功的@RequestBody都自动存入Request,不需要每个Controller手动写setAttribute,可以自定义参数解析器:
- 自定义参数解析器
import org.springframework.core.MethodParameter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import java.util.List; public class CacheRequestBodyArgumentResolver extends RequestResponseBodyMethodProcessor { public CacheRequestBodyArgumentResolver(List<HttpMessageConverter<?>> converters) { super(converters); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { Object body = super.resolveArgument(parameter, mavContainer, webRequest, binderFactory); // 参数解析完成直接存入请求属性,不需要等进入Controller方法 webRequest.setAttribute("requestBody", body, NativeWebRequest.SCOPE_REQUEST); return body; } }
- 配置SpringMVC替换默认的
@RequestBody解析器,之后在异常处理类里直接调用request.getAttribute("requestBody")就能拿到反序列化后的对象。
内容的提问来源于stack exchange,提问作者Supriyo Biswas
相关产品推荐
相关产品推荐

