请求/响应过滤器复用Entity Stream遇关闭异常,如何获取请求体?
解决JAX-RS过滤器中重复读取请求体的问题
问题原因
你遇到的「Entity stream is closed」异常,本质是因为InputStream是一次性读取的流:在请求过滤器中调用readAllBytes()后,流的指针已经走到末尾,且默认的实体流读取完成后会被关闭。当响应过滤器再次尝试获取这个流时,自然会报错。
解决方案
要在请求和响应过滤器中都拿到请求体,需要做两件事:
- 读取请求体后,将字节数据缓存起来
- 把一个可重复读取的流(比如
ByteArrayInputStream)放回请求上下文,同时将缓存的请求体字符串存入请求属性,供后续过滤器直接获取
修改后的代码示例
请求过滤器
@Override public void filter(ContainerRequestContext requestContext) throws IOException { // 读取原始实体流 InputStream originalStream = requestContext.getEntityStream(); byte[] entityBytes = originalStream.readAllBytes(); String requestBody = new String(entityBytes, StandardCharsets.UTF_8); // 将可重复读取的流放回请求上下文,保证后续处理能正常读取 requestContext.setEntityStream(new ByteArrayInputStream(entityBytes)); // 将请求体存入请求属性,响应过滤器直接从这里拿,不用再读流 requestContext.setProperty("REQUEST_BODY", requestBody); }
响应过滤器
@Override public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) { // 直接从请求属性中获取已缓存的请求体 String requestBody = (String) requestContext.getProperty("REQUEST_BODY"); // 这里就可以使用requestBody做后续处理了 }
补充说明
- 使用
ByteArrayInputStream是因为它支持重复读取,每次调用reset()或者重新获取流都能从头开始读 - 把请求体存入请求属性是最直接的方式,比响应过滤器再读流更高效,也避免了重复操作的问题
内容的提问来源于stack exchange,提问作者Monish Das
相关产品推荐
相关产品推荐

