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
类 对父类的 HttpServletRequestWrapper
的getInputStream()
方法进行重写,代码如下:
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的数值。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!