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

使用Interceptor做请求校验时出现Required request body is missing问题

问题原因及解决方案

问题根源

HttpServletRequest的输入流是一次性读取的,一旦调用getInputStream()读完流内容,流的指针就会移动到末尾,后续Spring MVC尝试读取请求体时,就会因为流中没有剩余内容而抛出Required request body is missing异常。你在拦截器里直接读取了原始请求的输入流,导致后续处理无法获取请求体。

另外你的代码还有个小问题:switch的case "V0"分支没有加break,会导致V0的逻辑执行完后,继续执行case "V1"/default的校验逻辑,这会重复读取已经到末尾的流,也会引发异常。

解决方案

1. 自定义可重复读取的RequestWrapper

创建一个包装类,把请求体缓存到字节数组中,让输入流可以重复读取:

public class RepeatableReadRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;

    public RepeatableReadRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        // 读取原始请求体并缓存到字节数组
        ServletInputStream inputStream = request.getInputStream();
        body = IOUtils.toByteArray(inputStream);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        // 返回新的ServletInputStream,从缓存的字节数组读取
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return byteArrayInputStream.available() == 0;
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
                // 空实现即可
            }

            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}

注:IOUtils.toByteArray()来自Apache Commons IO包,若没有该包,也可以手动循环读取流内容写入字节数组。

2. 用Filter提前包装请求(更稳妥)

在Filter里提前包装请求,确保整个请求生命周期内都使用可重复读取的请求实例:

@Component
public class RepeatableReadFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ServletRequest wrappedRequest = new RepeatableReadRequestWrapper((HttpServletRequest) request);
        chain.doFilter(wrappedRequest, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void destroy() {}
}

3. 修正拦截器代码

现在可以直接在拦截器里读取请求流,同时修复switch的break问题:

public class RequestInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        final ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        InputStream parameters = request.getInputStream();
        String contentType = request.getContentType();

        try {
            switch (contentType) {
                case "V0":
                    mapper.readValue(parameters, new TypeReference<JsonApi<CountryVo>>() {});
                    break; // 添加break避免执行后续分支
                case "V1":
                default:
                    mapper.readValue(parameters, new TypeReference<JsonApi<CountryVoV1>>() {});
            }
        } catch (final UnrecognizedPropertyException e) {
            throw new UnrecognizedRequestException();
        }

        return true;
    }

    // 其他方法保持不变
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 05:31:18