天天看點

Request中的InputStream隻能讀取一次的問題 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,才能實作替換
  • 攔截器一定要起作用(注冊别忘記),否則替換不到

閑暇時寫點東西,希望對你有幫助,謝謝!!!