欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Request中的InputStream只能读取一次的问题

程序员文章站 2022-03-10 12:09:30
...

Request中的InputStream只能读取一次的问题

拦截器中通过流 request.getInputStream()的方式读取body中传来的数据会导致controller接收不到值。

ps:在查看了一些InputStream可以重复读的方法,其实大多数都讲到了,但是没有讲全,原因和结尾没有说到,原本想复制粘贴完事,结果发现不行,还得自琢磨一下


问题的本质是InputStream读取完后指针已经到了内容的结尾,对象还是原来的对象,你再次读取的时候从最后开始读取,所以肯定是null

解决办法
  • 让指针读完后回到原来的起始点
  • 利用对象的思想将流的内容保存起来
1. 让指针读完后回到原来的起始点:利用mark做标记和reset回到标记点

由于mark和reset方法只能在BufferedInputStream中使用,在此不适用这个方法,不能直接用mark和reset

2. 利用对象的思想将流的内容保存起来:继承HttpServletRequestWrapper,替换请求过来的Request对象。

所以我们有:

public class RequestWrapper extends HttpServletRequestWrapper {
    private static final Logger logger = LoggerFactory.getLogger(RequestWrapper.class);
    private byte[] requestBody = null;//用于将流保存下来

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        requestBody = StreamUtils.copyToByteArray(request.getInputStream());
    }


    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {
            @Override
            public int read(){
                return bais.read();  // 读取 requestBody 中的数据
            }
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) { }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException{
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}

数据我们已经拿到,那么原来request的内容已经是不能继续读取了,所以我们要将传进来的Request替换,这样到controller拿到的才能继续去读取内容,所以我们要明白数据在容器的这个传输过程:

过程:

Tomcat容器--->Filter--->Servlet--->Interceptor---->controller

因为我是要在Interceptor中拿到数据来验证,所以我要在拦截器之前替换成可以多次读取的request对象,所以我要添加拦截器(继承Filter,注意要注册拦截器

拦截器

public class MyFilter implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(MyFilter .class);
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        logger.info("doFilter---21Line:到达拦截器!");
        if((request instanceof HttpServletRequest)) {
            logger.info("doFilter---23Line:创建新的request");
            requestWrapper = new RequestWrapper((HttpServletRequest) request);
        }

        if(requestWrapper == null) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);//替换
        }

    }

    @Override
    public void destroy() {

    }

注意的地方:

  • 问题是解决Request获取的InputStream只能读取一次的问题,如果是让InputStream读取多次,可以直接使用第一段代码就可以实现。
  • 对象要继承HttpServletRequestWrapper,才能实现替换
  • 拦截器一定要起作用(注册别忘记),否则替换不到

闲暇时写点东西,希望对你有帮助,谢谢!!!