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

今天在给一个项目加日志切面,在从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注解,原来的代码不需要做任何修改。

参考:获取ServletInputStream之后报错:java.io.IOException: Stream closed_OrangQceee的博客-CSDN博客_inputstream is closed 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值