Java如何解决发送Post请求报Stream closed问题
作者:胡安民 发布时间:2021-12-12 04:20:10
springboot项目还是ssm等java常用框架都会有这样的问题,解决办法通用
问题场景
前端发送Post请求,前端返回400 Bad Request,后端Controller层接口也没进去,然后我就开始分析,是啥问题,我通过后端控制台发现HttpMessageNotReadableException 提示信息,这个不是读取请求的消息错误发生的异常吗?
然后我通过IDEA 的DEBUG拦截这个异常发生的位置,然后将相关的代码从新走了一遍发现在
AbstractMessageConverterMethodArgumentResolver->readWithMessageConverters
EmptyBodyCheckingHttpInputMessage 是内部类
控制台打印warn 信息如下:
org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: Stream closed
问题分析
这是因为有人在过滤器或者 * 中对Request的请求体中的数据流读取了一遍导致的,Springboot准备读取Body数据映射到接口的实体类参数时候失败,发现流已经没有内容了,因为在接口数据流传输使用的都是InputStream 这个流只能被读取一次
解决办法
将InputStream 传输数据,缓存起来,保存到字符串中,之后用的时候将字符串转在转换为流,那么这个字符串就能持续的被复用了
缓存数据
package com.schemautils;
/**
* 解决获取post请求的请求体body只能读取一次问题
*/
import com.alibaba.fastjson.JSONObject;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
//防止未初始化body,我们手动初始化body ,内部会将body内容初始化到InputStream里
request.getParameterMap();
//然后在读取InputStream
inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public JSONObject getBody() {
return JSONObject.parseObject(this.body);
}
}
添加过滤器并且配置缓存类
package com.schemautils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
//获取请求中的流,将取出来的,再次转换成流,然后把它放入到新request对象中
//必须保证在所有过滤器之前执行,否则就会出现问题(按照首字母进行过滤器优先级A>B>C)
@WebFilter(filterName = "ACacheHttpServletRequestFilter", urlPatterns = "/")
public class CacheHttpServletRequestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(servletRequest instanceof HttpServletRequest) {
requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
}
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中
// 在chain.doFiler方法中传递新的request对象
if(null == requestWrapper) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
filterChain.doFilter(requestWrapper, servletResponse);
}
}
@Override
public void destroy() {
}
}
在启动类上开启扫描Filter注解
@SpringBootApplication(scanBasePackages = "com")
@ServletComponentScan //开启扫描Filter
public class ApplicatioBoot {
public static void main(String[] args) {
SpringApplication.run(ApplicatioBoot.class,args);
}
}
来源:https://blog.csdn.net/weixin_45203607/article/details/123708647


猜你喜欢
- 学习Java实现飞机航班管理系统,本文有该系统的功能截图,和数据库设计SQL语句供大家参考1.飞机航班管理系统背景本系统模拟飞机航班管理业务
- 在项目里,我需要做一个Spring Boot结合Thymeleaf前端模版,结合JPA实现分页的演示效果。做的时候发现有些问题,也查了现有网
- 本文实例讲述了Android实现ListView数据动态加载的方法。分享给大家供大家参考,具体如下:list.setOnScrollList
- 本文实例介绍了Android如何画出触摸轨迹的方法,分享给大家供大家参考,具体内容如下效果图:实现代码:package com.androi
- 域对象共享数据使用ServletAPI向request域对象共享数据@RequestMapping("/testServletAP
- 本文实例讲述了Java设计模式之 * 模式。分享给大家供大家参考,具体如下:前面介绍了静态代理模式, * 比静态代理模式更加强大。它能在
- 网上找的一个单页面通讯录,修改之后将添加联系人和修改/删除联系人分为两个独立页面MainActivitypackage com.exampl
- String:字符串类型1、构造函数。String() :构造一个空字符串对象。String(byte[] bytes) :通过byte数组
- 目录1、什么是LockSupport?2、两类基本API3、LockSupport本质4、LockSupport例子5、LockSuppor
- 安卓的开发从布局开始。安卓的界面编写也是使用xml进行布局的,一般如果熟悉了html界面的布局,那么很容易就能够理解安卓有关的布局了,这里介
- 类和结构的区别:1.一个是引用类型一个是值类型Class是引用类型,继承自System.Object类Struct是值类型,继承自Syste
- 本文实例为大家分享了Android自定义双向滑动控件的具体代码,供大家参考,具体内容如下先看一下效果图1.SeekBarPressure工具
- Eureka 采用 CS(Client/Server,客户端/服务器) 架构,它包括以下两大组件:Eureka Server:Eureka
- 验证码及它的作用验证码为全自动区分计算机和人类的图灵测试的缩写,是一种区分用户是计算机的公共全自动程序,这个问题可以由计算机生成并评判,但是
- 这篇文章主要介绍了java读取xml配置参数代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友
- 多点触摸技术在实际开发过程中,用的最多的就是放大缩小功能。比如有一些图片浏览器,就可以用多个手指在屏幕上操作,对图片进行放大或者缩小。再比如
- Java 中的 CyclicBarrier 是一种同步工具,它可以让多个线程在一个屏障处等待,直到所有线程都到达该屏障处后,才能继续执行。C
- 前言这个题目可能取得不大好,想了很久没想出更合适的了。在android开发webview的时候,有的时候后台不一定给的就是一个url,而是把
- 1.相关概念Spring Boot 默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 中的配置各种属性。建
- 这篇文章主要介绍了JAVA利用递归删除文件代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可