You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpringBoot项目中GlobalExceptionHandler如何正确获取RequestBody参数

问题根因

你遇到的null问题核心原因有两个:

  1. 异常发生在@RequestBody参数解析完成、进入Controller方法体之前,你写在Controller里的setAttribute逻辑还没有执行
  2. HttpServletRequest的输入流默认只能读取一次,Spring参数解析阶段已经把流读完了,就算你直接在异常处理类里读流也会拿到空内容

解决方案

方案1:缓存请求体(通用方案,适配所有异常场景)

通过过滤器把原始请求包装成可重复读取的ContentCachingRequestWrapper,缓存请求体内容,异常阶段直接从缓存读取。

  1. 实现请求缓存过滤器
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);
    }
}
  1. 启动类添加@ServletComponentScan注解,扫描刚才定义的过滤器
  2. 改造异常处理类读取缓存请求体
@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,可以自定义参数解析器:

  1. 自定义参数解析器
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;
    }
}
  1. 配置SpringMVC替换默认的@RequestBody解析器,之后在异常处理类里直接调用request.getAttribute("requestBody")就能拿到反序列化后的对象。

内容的提问来源于stack exchange,提问作者Supriyo Biswas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 12:12:03