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

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,才能实现替换
  • 拦截器一定要起作用(注册别忘记),否则替换不到

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

猜你喜欢

转载自blog.csdn.net/m0_37835884/article/details/103511845