HttpServletRequest.getInputStream() 多次獲取post的數據


HttpServletRequest.getInputStream() 多次獲取post的數據

在實際的開發過程中,我們會在Filter或者AOP中讀取body數據進行數據校驗,
GET方法獲取參數比較簡單。可以直接對HttpServletRequest類使用getQueryString和getParameterMap獲取到,但是對於POST方法,可使用如下方法從request中獲取body參數:

 private String getPostData(HttpServletRequest request) throws IOException {
    InputStream in = request.getInputStream();
    BufferedReader br = new BufferedReader(new InputStreamReader(in, Charset.forName("UTF-8")));
    StringBuffer sb = new StringBuffer("");
    String temp;
    while ((temp = br.readLine()) != null) {
        sb.append(temp);
    }
    if (in != null) {
        in.close();
    }
    if (br != null) {
        br.close();
    }
    return sb.toString();
}

這樣子雖然能夠獲取到post的數據,但是系統會報一個異常:

java.lang.IllegalStateException: getInputStream() has already been called ...

原來:

  • 一個InputStream對象在被讀取完成后,將無法被再次讀取,始終返回-1;
  • InputStream並沒有實現reset方法(可以重置首次讀取的位置),無法實現重置操作;

因此,當自己寫的Filter中調用了一次getInputStream()后,后面再調用getInputStream()讀取的數據都為空,所以才報IllegalStateException錯誤。

解決辦法

1、新建一個 MyRequestWrapper類 對父類的 HttpServletRequestWrappergetInputStream()方法進行重寫,代碼如下:

import com.tusdao.log.util.RequestParamAware;

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.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;


public class MyRequestWrapper extends HttpServletRequestWrapper {
    private final String body;
    public MyRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        StringBuilder sb = new StringBuilder();
        String line;
        BufferedReader reader = request.getReader();
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        body = sb.toString();
        request.setAttribute("body",body); //將post的body數據放入緩存,這樣子在后面就能夠隨時取用
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @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(this.getInputStream()));
    }
}

2、在攔截器中傳入Wrapper對象:

@Order(1)
@Component
@Slf4j
@WebFilter(filterName="logFilter", urlPatterns="/*")
public class LogFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        log.info("filter before");
        // 轉換成自己覆寫的類
        MyRequestWrapper req = new MyRequestWrapper((HttpServletRequest)request);
        log.info("獲取到post信息為:{}", RequestParamAware.extractPostBody(request));
        // 如果沒有覆寫HttpServletRequestWrapper
        // doFilter(request, response)之后 再用到body
        // 會拋出類似錯誤 Cannot call getInputStream(), getReader() already called
      	// 注意doFilter這里傳進去的參數req 而不是request !!!!
        chain.doFilter(req, response);
        log.info("filter after");
    }

    @Override
    public void destroy() {

    }
}

這樣,位於后面的controller就可以擁有唯一一次調用HttpServletRequest.getInputStream()的機會了。並且在后序的業務中可以通過getAttribute獲取到post的數值。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM