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

作者:胡安民 时间:2021-12-12 04:20:10 

springboot项目还是ssm等java常用框架都会有这样的问题,解决办法通用

问题场景

前端发送Post请求,前端返回400 Bad Request,后端Controller层接口也没进去,然后我就开始分析,是啥问题,我通过后端控制台发现HttpMessageNotReadableException 提示信息,这个不是读取请求的消息错误发生的异常吗?

然后我通过IDEA 的DEBUG拦截这个异常发生的位置,然后将相关的代码从新走了一遍发现在

AbstractMessageConverterMethodArgumentResolver->readWithMessageConverters

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

EmptyBodyCheckingHttpInputMessage 是内部类

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

控制台打印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,Post,Stream,closed
0
投稿

猜你喜欢

  • c#实现服务器性能监控并发送邮件保存日志

    2023-09-10 11:56:14
  • C#操作注册表的方法

    2022-06-21 19:14:06
  • obix协议在java中的配置和使用详解

    2023-11-25 20:59:42
  • 基于ElasticSearch Analyzer的使用规则详解

    2023-09-28 14:41:04
  • 解决android studio 打开java文件 内容全变了的问题

    2022-03-18 19:31:44
  • 关于JAVA11中图片与BASE64相互转换的实现

    2022-12-06 06:44:11
  • 一篇文章带你入门Java修饰符

    2021-12-25 12:46:01
  • Java8中stream和functional interface的配合使用详解

    2023-02-27 22:44:51
  • Java实现一个简易聊天室流程

    2023-10-02 19:55:53
  • 用Flutter开发自定义Plugin的方法示例

    2023-07-05 00:19:40
  • 零基础入门SpringMVC拦截器的配置与使用

    2023-07-17 21:59:28
  • JDBC查询Map转对象实现过程详解

    2021-08-30 07:17:40
  • QT自定义QTextEdit实现大数据的实时刷新显示功能实例

    2023-05-06 03:18:28
  • c#滚动字幕动画窗体制作步骤

    2023-12-06 20:12:58
  • Android PowerManagerService省电模式策略控制

    2023-11-25 02:46:53
  • java解析XML详解

    2023-02-26 09:19:51
  • 详解C#使用AD(Active Directory)验证内网用户名密码

    2023-03-03 23:17:47
  • SpringIOC框架的简单实现步骤

    2021-10-28 21:44:35
  • Kotlin实用语法糖空安全类型转换及相等性判断

    2021-09-03 20:23:49
  • 使用Postman传递arraylist数据给springboot方式

    2022-08-27 01:13:01
  • asp之家 软件编程 m.aspxhome.com