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

 更新时间:2022年06月13日 09:07:01   作者:胡安民  
这篇文章主要介绍了Java如何解决发送Post请求报Stream closed问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

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);
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Spring MVC @GetMapping和@PostMapping注解的使用方式

    Spring MVC @GetMapping和@PostMapping注解的使用方式

    这篇文章主要介绍了Spring MVC @GetMapping和@PostMapping注解的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • SpringBoot返回文件使前端下载的几种方式小结

    SpringBoot返回文件使前端下载的几种方式小结

    本文主要介绍了Spring Boot中几种文件下载的方法,通过后端应用下载文件并进行业务处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-11-11
  • Java 如何在switch case语句中声明变量

    Java 如何在switch case语句中声明变量

    这篇文章主要介绍了Java 如何在switch case语句中声明变量,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • ElasticSearch的安装与基本概念

    ElasticSearch的安装与基本概念

    这篇文章主要介绍了ElasticSearch的安装与基本概念,提供了一个分布式多用户能力的全文搜索引擎,Elasticsearch是用Java开发的,需要的朋友可以参考下
    2023-04-04
  • 浅谈java web中常用对象对应的实例化接口

    浅谈java web中常用对象对应的实例化接口

    下面小编就为大家带来一篇浅谈java web中常用对象对应的实例化接口。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • 谈谈java的concurrent用法

    谈谈java的concurrent用法

    这篇文章给大家介绍java的concurrent用法,感兴趣的朋友一起学习吧
    2015-10-10
  • SpringBoot+Redis执行lua脚本的方法步骤

    SpringBoot+Redis执行lua脚本的方法步骤

    这篇文章主要介绍了SpringBoot+Redis执行lua脚本的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • 详解如何使用Mybatis的拦截器

    详解如何使用Mybatis的拦截器

    MyBatis 拦截器是 MyBatis 提供的一个强大特性,它允许你在 MyBatis 执行其核心逻辑的关键节点插入自定义逻辑,从而改变 MyBatis 的默认行为,本文给大家详细介绍了如何使用Mybatis的拦截器,需要的朋友可以参考下
    2024-03-03
  • Java8新特性之Collectors.joining()实例详解

    Java8新特性之Collectors.joining()实例详解

    在项目中我们常常要对list集合的数据做一些字符串拼接/处理等相关操作,下面这篇文章主要给大家介绍了关于Java8新特性之Collectors.joining()的相关资料,需要的朋友可以参考下
    2023-01-01
  • SpringBoot在IDEA中实现热部署(JRebel实用版)

    SpringBoot在IDEA中实现热部署(JRebel实用版)

    这篇文章主要介绍了SpringBoot在IDEA中实现热部署(JRebel实用版),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05

最新评论