一、 背景
有时候我们的请求是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加入即可