天天看點

解決HttpServletRequest輸入流隻能讀一次的問題

為什麼HttpServletRequest的輸入流隻能讀一次呢?

當我們調用

getInputStream()

方法擷取輸入流時得到的是一個InputStream對象,而實際類型是ServletInputStream,它繼承于InputStream。

InputStream的

read()

方法内部有一個postion,标志目前流被讀取到的位置,每讀取一次,該标志就會移動一次,如果讀到最後,

read()

會傳回-1,表示已經讀取完了。如果想要重新讀取則需要調用

reset()

方法,position就會移動到上次調用mark的位置,mark預設是0,是以就能從頭再讀了。調用

reset()

方法的前提是已經重寫了

reset()

方法,當然能否reset也是有條件的,它取決于

markSupported()

方法是否傳回true。

InputStream預設不實作reset的相關方法,而ServletInputStream也沒有重寫reset的相關方法,這樣就無法重複讀取流,這就是我們從request對象中擷取的輸入流就隻能讀取一次的原因。

解決辦法:重寫HttpServletRequestWrapper方法

通過重寫HttpServletRequestWrapper把request的儲存下來,然後通過過濾器儲存下來的request在填充進去,這樣就可以多次讀取request了。

重寫HttpServletRequestWrapper

package com.aliyun.icc.core.config.httpHelp;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;


public class BodyReaderHttpServletRequestWrapper extends
        HttpServletRequestWrapper {

    private final byte[] body;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        Enumeration e = request.getHeaderNames()   ;
        while(e.hasMoreElements()){
            String name = (String) e.nextElement();
            String value = request.getHeader(name);
            System.out.println(name+" = "+value);

        }
        body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
    }

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

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);

        return 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 bais.read();
            }
        };
    }

    @Override
    public String getHeader(String name) {
        return super.getHeader(name);
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        return super.getHeaderNames();
    }

    @Override
    public Enumeration<String> getHeaders(String name) {
        return super.getHeaders(name);
    }

}      

再編寫過濾器

package com.aliyun.core.config.filter; 
import com.aliyun.core.config.httpHelp.BodyReaderHttpServletRequestWrapper; 
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
 
public class HttpServletRequestReplacedFilter implements Filter {
    @Override
    public void destroy() {
 
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if(request instanceof HttpServletRequest &&
                (null == request.getParameterMap() || request.getParameterMap().isEmpty())) {
            requestWrapper = new RequestReaderHttpServletRequestWrapper((HttpServletRequest) request);
        }
        //擷取請求中的流如何,将取出來的字元串,再次轉換成流,然後把它放入到新request對象中。
        // 在chain.doFiler方法中傳遞新的request對象
        if(requestWrapper == null) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }
 
    @Override
    public void init(FilterConfig arg0) throws ServletException {
 
    }
 
}