使用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
相关产品推荐
相关产品推荐

