解决request.getInputStream()只能读取一次的问题

问题描述

由于在拦截器中读取了输入流的数据,在request中的输入流只能读取一次,请求进去Controller时,输入流中已经没有数据了,导致获取不到数据。

原因

  • 一个InputStream对象在被读取完成后,将无法被再次读取,始终返回-1;

  • InputStream并没有实现reset方法(可以重置首次读取的位置),无法实现重置操作;

解决方法

继承HttpServletRequestWrapper以实现在Filter中修改HttpServletRequest的参数

import org.apache.commons.io.IOUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 包装HttpServletRequest
 */
public class MyServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    public MyServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = IOUtils.toByteArray(super.getInputStream());
    }

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

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new RequestBodyCachingInputStream(body);
    }

    private class RequestBodyCachingInputStream extends ServletInputStream {
        private byte[] body;
        private int lastIndexRetrieved = -1;
        private ReadListener listener;

        public RequestBodyCachingInputStream(byte[] body) {
            this.body = body;
        }

        @Override
        public int read() throws IOException {
            if (isFinished()) {
                return -1;
            }
            int i = body[lastIndexRetrieved + 1];
            lastIndexRetrieved++;
            if (isFinished() && listener != null) {
                try {
                    listener.onAllDataRead();
                } catch (IOException e) {
                    listener.onError(e);
                    throw e;
                }
            }
            return i;
        }

        @Override
        public boolean isFinished() {
            return lastIndexRetrieved == body.length - 1;
        }

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

        @Override
        public void setReadListener(ReadListener listener) {
            if (listener == null) {
                throw new IllegalArgumentException("listener cann not be null");
            }
            if (this.listener != null) {
                throw new IllegalArgumentException("listener has been set");
            }
            this.listener = listener;
            if (!isFinished()) {
                try {
                    listener.onAllDataRead();
                } catch (IOException e) {
                    listener.onError(e);
                }
            } else {
                try {
                    listener.onAllDataRead();
                } catch (IOException e) {
                    listener.onError(e);
                }
            }
        }

        @Override
        public int available() throws IOException {
            return body.length - lastIndexRetrieved - 1;
        }

        @Override
        public void close() throws IOException {
            lastIndexRetrieved = body.length - 1;
            body = null;
        }
    }
}

通过过滤器进行包装request对象:

import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 替换Request对象
 */
@Component
public class RequestReplaceFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (!(request instanceof MyServletRequestWrapper)) {
            request = new MyServletRequestWrapper(request);
        }
        filterChain.doFilter(request, response);
    }
}

推荐阅读更多精彩内容

  • 很期待在打拼的城市有一所房子,每一张装修好的我喜欢的照片,我都收藏着,希望哪一天可以按照这样的风格,来装饰自己的小...
    袁夏天阅读 29评论 0 0
  • 我的一位同学即将从法院辞职,进入大学任教了。他在我面前热情洋溢地说了常常的一番话,中心词是梦想,大致意思是:...
    金不换007阅读 363评论 0 2
  • 《但丁密码》围绕一个问题,人口过多引起一系列问题,最终,影响人类的生存问题。于是,衍生出一派人,产生了这样的念头:...
    简宁直树阅读 28评论 1 2
  • 又是一个新的学期,开会,领课程表,领课本,挪办公室,打扫卫生,忙了一晌,终于可以松口气了,梅子坐在办公桌前...
    飘雨_3ef3阅读 82评论 0 0
  • 今天打电话联系几家做保温阳台的公司,打算定制保温阳台。在咨询过程中比较注重性价比,沟通了好几家感觉都不是很专业。 ...
    耕耘生活阅读 39评论 0 9