使用ContainerRequestFilter时合法JSON请求抛出400错误的问题排查
问题排查与解决方案
问题原因
你的过滤器核心问题是请求流只能被读取一次:
- 在过滤器中,你使用
CloseShieldInputStream读取了请求流内容,但读取完成后,流的指针已移动到末尾。 - 后续JAX-RS框架处理
MatchResource的processRequest方法时,需要再次读取请求流来解析AppRequest对象,但此时流已无可读数据,导致解析失败,抛出400 Bad Request错误。 CloseShieldInputStream仅能防止流被提前关闭,无法解决流无法重复读取的问题。
修复方案
需要在过滤器中复制请求流内容,确保后续处理能重新读取完整的请求数据。修改后的过滤器代码如下:
@Provider public class AppFilter implements ContainerRequestFilter { private static final Logger LOG = Logger.getLogger(AppFilter.class); @Inject ObjectMapper objectMapper; @Override public void filter(ContainerRequestContext context) throws IOException { InputStream originalStream = context.getEntityStream(); // 复制请求流内容到字节数组,保留原始数据 ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = originalStream.read(buffer)) != -1) { baos.write(buffer, 0, bytesRead); } byte[] requestContent = baos.toByteArray(); // 使用复制的内容进行JSON解析校验 try (InputStream stream = new ByteArrayInputStream(requestContent)) { objectMapper.readValue(stream, AppRequest.class); } catch (Exception ex) { LOG.error(ExceptionUtils.getRootCauseMessage(ex)); } // 将复制后的流重新设置回请求上下文,供后续处理使用 context.setEntityStream(new ByteArrayInputStream(requestContent)); } }
额外说明
- 该方案通过将请求流内容复制到字节数组,创建可重复读取的
ByteArrayInputStream,既完成了JSON解析错误的日志记录,又保证了后续资源方法能正常读取请求数据。 - 若请求体过大,可考虑使用临时文件存储流内容以避免内存占用过高,常规业务场景下字节数组方式足够高效。
内容的提问来源于stack exchange,提问作者Pracheer Pancholi
相关产品推荐
相关产品推荐

