机器学习 Wendy sed colors swift3 vue标签 python转16进制 录音棚设备一套多少钱 cos图像和sin图像 oracle添加索引 mysql分页查询sql语句 python正则匹配中文 python数据格式 python安装配置 java实现 java写文件 corelpainter 右键菜单背景 音频频谱分析软件 java疯狂讲义 笔记本外接显示器好吗 海妖花粉哪里多 矩阵分析与应用 批处理if 爱奇艺无法投屏 数组求和 红米手机怎么连接电脑 拳皇2005出招表 qq浏览器全屏 camworks 苹果手机怎么添加邮箱 快剪辑去水印 快手封号规则 officerecovery vpstudio lol皮肤修改器 qq群文件下载失败 黑色火山怎么打 vs2017官网 快递查询自动识别公司
当前位置: 首页 > 学习教程  > 编程语言

获取ServletInputStream之后报错:java.io.IOException: Stream closed

2021/1/13 20:40:50 文章标签: 测试文章如有侵权请发送至邮箱809451989@qq.com投诉后文章立即删除

今天在给一个项目加日志切面,在从request里面获取body参数时,报错:java.io.IOException: Stream closed。 先看一下相关代码。 private String printToLogStringByRequestBody(HttpServletRequest request) {String body "";Ser…

今天在给一个项目加日志切面,在从request里面获取body参数时,报错:java.io.IOException: Stream closed。

先看一下相关代码。

private String printToLogStringByRequestBody(HttpServletRequest request) {
        String body = "";
        ServletInputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            body = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
        } catch (IOException e) {
            log.error("body to string is error !", e);
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
        return body;
    }

报错信息很简单,就是说从inputStream获取body时,该字节流已关闭,无法再读取。打断点可以看到closed这个属性是true。
在这里插入图片描述
首先我肯定没有对它进行手动关闭。但是有一个很奇怪的地方,在调用一部分接口的时候不会报错,报错的接口有一个共同点,就是参数加了@RequestBody注解。随后我就在网上查了一下,基本上确定了问题就是出在这个注解上。

stackoverflow上面有个回答解释了这个问题。

You use

@RequestBody User user
for first case, so Spring framework already
processed input stream of HttpServletRequest instance (can happen only
once by default), parsed data and injected user instance to your
handler.

So input stream is closed when handler is called. You can overload
attemptAuthentication method and pass user instance as a param
instead.

I suppose you don’t have any Spring mappings for second case, so input
stream stays “untouched” and you are able to process it within the
authentication method.

翻译一下就是:

对于第一种情况,Spring框架已经处理了HttpServletRequest实例的输入流(默认情况下只能发生一次),解析数据并将user实例注入到处理程序中。
因此,在调用处理程序时,输入流将关闭。 你可以重载ttemptAuthentication方法,然后将user实例作为参数传递。
我想你在第二种情况下没有任何Spring映射,因此输入流保持“不变”,并且您可以在身份验证方法中对其进行处理。

stackoverflow上相关的问题参考

所以我们要做的就是写一个RequestWrapperrequest中的输入流保存一下。

下面是代码:

@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    /**
     * @description 将request中输入流中的内容保存起来
     * @param request HttpServletRequest
     */
    public RequestWrapper(HttpServletRequest request) {
        super(request);
        byte[] bytes = null;
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            bytes = IOUtils.toByteArray(inputStream);
        } catch (IOException e) {
            log.error("requestWrapper error", e);
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
        body = bytes;
    }

    /**
     * @description 重写getInputStream,返回保存在属性中的body
     * @return javax.servlet.ServletInputStream
     */
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

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

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }
}
@Component
@ServletComponentScan
public class HttpServletFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    /**
     * @description 将自定义的ServletRequest替换进filterChain中,使request可以重复读取
     * @param servletRequest servletRequest
     * @param servletResponse servletResponse
     * @param filterChain filterChain
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ServletRequest request = null;
        if (servletRequest instanceof HttpServletRequest) {
            request = new RequestWrapper((HttpServletRequest) servletRequest);
        }
        if (request != null) {
            filterChain.doFilter(request, servletResponse);
        } else {
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    @Override
    public void destroy() {

    }
}

注意加上@Component@ServletComponentScan注解,原来的代码不需要做任何修改。


本文链接: http://www.dtmao.cc/news_show_600397.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?