繼承HttpServletRequestWrapper 實現request中流的重複獲取

業務場景:需要打印所有的api請求的信息到log中,在Filter中攔截了所有的api請求,但是打印的信息中需要包含api請求的body,如果在Filter中使用request.getInputStream()來獲取流來得到body中的信息,可以達到預期效果,但是流的獲取只能獲取一次,之後再獲取就獲取不到了,導致controller無法拿到參數。
解決辦法:HttpServletRequestWrapper,該類是HttpServletRequest的封裝類,我們可以自定義一個類,繼承HttpServletRequestWrapper,重寫其中的getInputStream方法,讓其可以重複獲取我們想要的流,並且在Filter中的filterChain.doFilter(ServlerRequest, ServletResponse)方法中,傳入我們的自定義類的實例,而不是原來的HttpServletRequest對象,由於我們的類是繼承自HttpServletRequestWrapper類,所以當參數傳入此方法是沒有問題的。由於我們自定義類中的getInputStream方法已經被重寫爲可多次獲取的版本,所以也就不用擔心controller無法獲取到數據。
具體實現:
//自定義的ServletRequest類
import org.apache.commons.io.IOUtils;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

public class MyResettableServletRequest extends HttpServletRequestWrapper {
    //保存流中的數據
    private byte[] data;

    public MyResettableServletRequest(HttpServletRequest request) throws Exception{
        super(request);
//從流中獲取數據
        data = IOUtils.toByteArray(request.getInputStream());
    }

    public ServletInputStream getInputStream(){
//在調用getInputStream函數時,創建新的流,包含原先數據流中的信息,然後返回
        return new MyServletInputStream(new ByteArrayInputStream(data));
    }

    class MyServletInputStream extends ServletInputStream{
        private InputStream inputStream;

        public MyServletInputStream(InputStream inputStream){
            this.inputStream = inputStream;
        }

        @Override
        public int read() throws IOException {
            return inputStream.read();
        }
    }
}


//在Filter中使用 
@Override 
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 
HttpServletRequest request = (HttpServletRequest) servletRequest; 
MyResettableServletRequest myRequest = new MyResettableServletRequest(request); 
String bodyParam = IOUtils.toString(myRequest.getInputStream());
/** 
* 打印log 
*/ 
filterChain.doFilter(myRequest, servletResponse); 
}


總結:原生的HttpServletRequest類是有許多的缺陷的,比如無法重複獲取其中的流,無法通過setAttribute方法來改寫屬性,當我們遇到這樣的情況時,一個很好的解決辦法就是使用裝飾者模式,重寫一個類,繼承自Wrapper類,將其中的方法重寫以滿足我們的需求。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章