繼承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類,將其中的方法重寫以滿足我們的需求。
解決辦法: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類,將其中的方法重寫以滿足我們的需求。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.