服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Java如何解决发送Post请求报Stream closed问题

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

2023-02-24 14:51胡安民 Java教程

这篇文章主要介绍了Java如何解决发送Post请求报Stream closed问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

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教程Java模拟栈和队列数据结构的基本示例讲解

    Java模拟栈和队列数据结构的基本示例讲解

    这篇文章主要介绍了Java模拟栈和队列数据结构的基本示例,栈的后进先出和队列的先进先出是数据结构中最基础的知识,本文则又对Java实现栈和队列结构的方...

    匆忙拥挤repeat4532020-04-17
  • Java教程Java 反射机制的实例详解

    Java 反射机制的实例详解

    这篇文章主要介绍了Java 反射机制的实例详解的相关资料,希望通过本文能帮助到大家,让大家理解掌握反射机制,需要的朋友可以参考下...

    子沐阿7822021-01-18
  • Java教程Java多线程-线程的同步与锁的问题

    Java多线程-线程的同步与锁的问题

    线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。本篇文章主要介绍了Java多线程-线程的同步与锁的问题,有兴趣的可以了解一下...

    Ruthless5472020-07-05
  • Java教程Java实现用Mysql存取图片操作实例

    Java实现用Mysql存取图片操作实例

    这篇文章主要介绍了Java实现用Mysql存取图片操作实例,本文讲解了使用BLOB类型保存和读取图片的代码实例,需要的朋友可以参考下 ...

    junjie4112019-12-24
  • Java教程Java 自定义线程池和线程总数控制操作

    Java 自定义线程池和线程总数控制操作

    这篇文章主要介绍了Java 自定义线程池和线程总数控制操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    JAVA前线11782021-08-16
  • Java教程Java中数组协变和范型不变性踩坑记录

    Java中数组协变和范型不变性踩坑记录

    数组的协变性来源于数组的一个优势,这篇文章主要给大家介绍了关于Java中数组协变和范型不变性踩坑的一些内容,文中通过示例代码介绍的非常详细,对...

    左之了8152021-07-17
  • Java教程基于断点续传下载原理的实现

    基于断点续传下载原理的实现

    下面小编就为大家带来一篇基于断点续传下载原理的实现。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    peachyy12562020-12-28
  • Java教程Java日常练习题,每天进步一点点(55)

    Java日常练习题,每天进步一点点(55)

    下面小编就为大家带来一篇Java基础的几道练习题(分享)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望可以...

    牛哄哄的柯南10862021-11-19