Java如何解决发送Post请求报Stream closed问题

网友投稿 741 2022-07-24


目录问题场景问题分析解决办法

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 kUZVvRTA,内部会将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);

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:dubbo自定义异常的完整步骤与测试
下一篇:springboot集成KoTime的配置过程
相关文章

 发表评论

暂时没有评论,来抢沙发吧~