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

Spring校验未通过时如何获取完整请求体?

解决Spring MVC校验异常时获取完整请求体的方案

默认情况下,Spring MVC的请求输入流只能读取一次,@RequestBody注解已经消费了请求体,当校验失败抛出MethodArgumentNotValidException时,异常处理器无法再次读取请求体。以下是两种可行的解决方法:

方法一:通过Filter全局缓存请求体

这种方式适合所有接口统一处理,无需修改Controller代码。

  1. 自定义请求体缓存Filter
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RequestBodyCachingFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 将请求包装为可缓存请求体的对象
        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
        filterChain.doFilter(wrappedRequest, response);
    }
}
  1. 注册Filter到Spring容器
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<RequestBodyCachingFilter> requestBodyCachingFilter() {
        FilterRegistrationBean<RequestBodyCachingFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new RequestBodyCachingFilter());
        registrationBean.addUrlPatterns("/*");
        // 设置最高优先级,确保在Spring MVC的其他过滤器前执行
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return registrationBean;
    }
}
  1. 在异常处理器中读取缓存的请求体
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object handleValidationException(MethodArgumentNotValidException ex, HttpServletRequest request) {
        String requestBody = "";
        if (request instanceof ContentCachingRequestWrapper) {
            ContentCachingRequestWrapper wrappedRequest = (ContentCachingRequestWrapper) request;
            byte[] bodyBytes = wrappedRequest.getContentAsByteArray();
            requestBody = new String(bodyBytes, StandardCharsets.UTF_8);
        }

        // 整理校验错误信息
        String errorMsg = ex.getBindingResult().getFieldErrors().stream()
                .map(error -> error.getField() + ": " + error.getDefaultMessage())
                .collect(Collectors.joining("; "));

        // 返回包含请求体和错误信息的响应
        return "{\"requestBody\": \"" + requestBody + "\", \"errorMsg\": \"" + errorMsg + "\"}";
    }
}

方法二:在Controller方法中直接获取缓存请求体

如果只需要针对特定接口处理,可以直接在Controller方法中注入ContentCachingRequestWrapper,读取缓存的请求体并暂存,异常处理器中取出使用。

  1. Controller方法中保存请求体
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;

@RestController
public class DemoController {

    @PostMapping("/demo")
    public String demo(@Validated @RequestBody DemoPOJO pojo, ContentCachingRequestWrapper request) {
        // 读取缓存的请求体并存入请求属性
        String requestBody = new String(request.getContentAsByteArray(), StandardCharsets.UTF_8);
        request.setAttribute("REQUEST_BODY", requestBody);
        // 业务逻辑处理
        return "success";
    }
}
  1. 异常处理器中取出请求体
@ExceptionHandler(MethodArgumentNotValidException.class)
public Object handleValidationException(MethodArgumentNotValidException ex, HttpServletRequest request) {
    String requestBody = (String) request.getAttribute("REQUEST_BODY");
    String errorMsg = ex.getBindingResult().getFieldErrors().stream()
            .map(error -> error.getField() + ": " + error.getDefaultMessage())
            .collect(Collectors.joining("; "));
    return "{\"requestBody\": \"" + requestBody + "\", \"errorMsg\": \"" + errorMsg + "\"}";
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 11:46:08