一、 背景
有時候我們的請求是post,但我們又要對參數簽名,這個時候我們需要獲取到body的信息,但是當我們使用*HttpServletRequest
的getReader()
和getInputStream()
獲取參數后,后面不管是框架還是自己想再次獲取body已經沒辦法獲取。當然也有一些其他的場景,可能需要多次獲取的情況。
可能拋出類似以下的異常
java.lang.IllegalStateException: getReader() has already been called for this request
二、spring中的ContentCachingRequestWrapper
spring中的ContentCachingRequestWrapper提供了getContentAsByteArray()
方法用來多次讀取body。
getContentAsByteArray()
消費了InputStream來緩存請求體。導致該方法不能多次使用getReader()
和getInputStream()
。所以該方法並不通用。
三、 自定義擴展
1. 擴展HttpServletRequest
創建一個自定義實現HttpServletRequest
的類,步驟如下
1.1. 創建一個自定義類
需要繼承HttpServletRequestWrapper
並且寫一個構造函數來緩存body數據
public class CustomHttpServletRequest extends HttpServletRequestWrapper {
private byte[] cachedBody;
public CustomHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
InputStream is = request.getInputStream();
this.cachedBody = StreamUtils.copyToByteArray(is);
}
}
1.2. 重寫getReader()
@Override
public BufferedReader getReader() throws IOException {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
return new BufferedReader(new InputStreamReader(byteArrayInputStream));
}
1.3. 重寫getInputStream()
@Override
public ServletInputStream getInputStream() throws IOException {
return new CachedBodyServletInputStream(this.cachedBody);
}
CachedBodyServletInputStream
參考下面步驟
2. 實現ServletInputStream
創建一個繼承了ServletInputStream
的類
public class CachedBodyServletInputStream extends ServletInputStream {
private InputStream cachedBodyInputStream;
public CachedBodyServletInputStream(byte[] cachedBody) {
this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody);
}
@Override
public boolean isFinished() {
try {
return cachedBodyInputStream.available() == 0;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}
@Override
public int read() throws IOException {
return cachedBodyInputStream.read();
}
}
3. 創建一個Filter加入到容器中
既然要加入到容器中,可以創建一個Filter,然后加入配置
我們可以簡單的繼承OncePerRequestFilter
然后實現下面方法即可。
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
CustomHttpServletRequest customHttpServletRequest =
new CustomHttpServletRequest(httpServletRequest);
filterChain.doFilter(customHttpServletRequest, httpServletResponse);
}
然后添加該Filter加入即可