java.io.IOException: Stream closed,关于设置拦截器获取post请求的参数,出现的流关闭问题

这篇具有很好参考价值的文章主要介绍了java.io.IOException: Stream closed,关于设置拦截器获取post请求的参数,出现的流关闭问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

问题描述:

就是最近在做接口拦截,判断post请求提交的参数有没有敏感词。但是在处理的过程中,就遇到了java.io.IOException: Stream closed这种类型的报错,其原因就是流只能被访问一次,在拦截器那边被访问之后,流就已经被关闭了,等到controller层再获取参数的时候,就会报错,无法获取参数。

解决思路:

1、添加过滤组件,你只能取出数据的同时,复制一份再放回去。
2、需要主要的是,复制流这个操作仅限于body数据格式(也就是Content-Type: application/json)是JSON的情况。如果是form-urllencoded这种key-value格式的,不能走复制流,否则controller会拿不到参数。如果是form表单(Content-Type: multipart/form-data)的情况,需要将流转换成MultipartFile格式,要不然在controller层依旧会取不到上传的文件数据。

代码展示:

1、添加一个工具类,获取到post请求的body
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * HttpContextUtils
 *
 * @author taojuan
 */
public class HttpContextUtils extends HttpServletRequestWrapper {

    /**
     * 输入流
     */
    private final byte[] bytes;

    public HttpContextUtils(HttpServletRequest request) throws IOException {
        super(request);

	byteArrayOutputStream.toByteArray()
        bytes = getBodyString(request).getBytes(StandardCharsets.UTF_8);

    }


    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    public String getBodyString(final ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        try (
                InputStream inputStream = cloneInputStream(request.getInputStream());
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))
        ) {
            String line = "";
            while (Objects.nonNull((line = reader.readLine()))) {
                sb.append(line);
            }
        } catch (IOException e) {
            throw new RuntimeException("输入流读取出错");
        }
        return sb.toString();
    }

    /**
     * 输入流复制
     *
     * @param inputStream
     * @return
     */
    private InputStream cloneInputStream(ServletInputStream inputStream) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = inputStream.read(buffer)) > -1) {
                byteArrayOutputStream.write(buffer, 0, len);
            }
            byteArrayOutputStream.flush();
        } catch (IOException e) {
            throw new RuntimeException("复制输入流读取出错");
        }
        return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    /**
     * 重写父方法,返回新的输入流
     *
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream copyStream = new ByteArrayInputStream(bytes);
        /**
         * 新的输入流
         */
        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return copyStream.read();
            }

            /**
             * 未读状态
             * @return
             */
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
}

2、添加一个过滤器
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * <b><code>HttpServelRequestFilter</code></b>
 * <p/>
 * Description
 * <p/>
 * <b>Creation Time:</b> 2022/12/2 上午11:41.
 *
 * @author taojuan
 * @since cmicsed-dyprdp-be 0.2.0
 */
@WebFilter(urlPatterns = {"/*"})
@Slf4j
public class HttpServletRequestFilter implements Filter {
    // {"/api/v1/closedcircle/*","/api/v1/qualityService/*","/api/v1/qualityLightOn/*","/api/v1/audiocenter/*","/api/v1/listenaudio/comment"}

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        String token = request.getHeader("token");
        response.setHeader("token", token);
        String contentType = servletRequest.getContentType();
        String method = "multipart/form-data";

        if (contentType != null && contentType.contains(method)) {
            //重点!!!!!这里通过spring的轮子,实现request的转换,
            MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
            MultipartHttpServletRequest multipartRequest = resolver.resolveMultipart(request);
            // 将转化后的 request 放入过滤链中
            request = multipartRequest;

        }

        ServletRequest requestWrapper = new HttpContextUtils(request);

        try {
            log.info("获取到的输入流:{}", requestWrapper.getInputStream());
        } catch (Exception e) {
            log.error("异常:", e);
        }
        filterChain.doFilter(requestWrapper, servletResponse);

    }

    @Override
    public void destroy() {

    }


}
3、在拦截器里获取post请求的body,进行一些敏感词的判断拦截

主要是调用这个办法: String postParam = new HttpContextUtils(httpServletRequest).getBodyString(httpServletRequest);

@Component
@Slf4j
public class ApiInterceptor implements HandlerInterceptor {
 @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        // 从 http 请求头中取出签名
        try {
            //post请求,body中的参数
            String postParam = new HttpContextUtils(httpServletRequest).getBodyString(httpServletRequest);

            log.info("获取到的post请求参数:{}", postParam);

            List<String> list = new ArrayList<>();

            if (!postParam.isEmpty()) {
                List<String> temp = wordMapper.selectSensitiveWord();

                //对比敏感词库,是否有敏感词
                if (postParam != null && postParam.trim().length() > 0) {
                    for (String str : temp) {
                        if (postParam.contains(str)) {
                            list.add(str);
                        }
                    }
                }
            }

            if (!list.isEmpty()) {
                // throw new IOException("您发送请求中的参数中含有非法字符");
                throw new MeshNetwornException("您发送请求中的参数中含有非法字符", list);
            }
        } catch (IOException io) {
            throw new IOException("您发送请求中的参数中含有非法字符");
        } catch (MeshNetwornException e) {
            throw new MeshNetwornException("您发送请求中的参数中含有敏感字符", e.getData());
        } catch (Exception e) {
            e.printStackTrace();
            return true;
        }

        return true;

    }

}
4、最后需要在启动类上添加注解,开启过滤组件
@ServletComponentScan

如果过滤不起作用,可在组件扫描那里加上HttpServletRequestFilter这个过滤器所在的目录,如下图,我的文件放在该包下

@ServletComponentScan("com.commons.component")
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
		TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
	}
}
5、如果遇到java.lang.ClassNotFoundException: org.apache.commons.fileupload.disk.DiskFileItemFactory这个问题,可能就是pom文件确实下面的依赖包,添加即可。
<dependency>
   <groupId>commons-fileupload</groupId>
   <artifactId>commons-fileupload</artifactId>
   <version>1.2.1</version>
 </dependency>

上面就是我遇到Stream closed问题解决的所有过程了,问题不同,仅供参考。
下面是解决问题时,主要参考的几个博文:
1、http://t.csdn.cn/NbNUB
2、http://t.csdn.cn/4N3Th
3、http://t.csdn.cn/fNaAr文章来源地址https://www.toymoban.com/news/detail-588287.html

到了这里,关于java.io.IOException: Stream closed,关于设置拦截器获取post请求的参数,出现的流关闭问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java.io.IOException: Your InputStream was neither an OLE2 stream, nor an OOXML stream

    流读取文件时可能报以下错误: 此错误一般是文件上传时,用Workbook工厂或者实体类直接创建new HSSFWorkbook/XSSFWorkbook时出现。 解决方法很简单有两种: 1、在项目的pom.xml文件内新增或补足以下代码块 2、用更简单的cn.hutool.poi.excel.ExcelReader类,样例如下:

    2024年04月13日
    浏览(22)
  • java.io.IOException: unexpected end of stream on 以及java.io.EOFException: \n not found: limit=0

    java.io.IOException: unexpected end of stream on java.io.OKHttp Caused by: java.io.EOFException: n not found: limit=0 content=… 在一次使用OkHttp对接两个第三方接口的时候遇到了这个报错,情景是这样的,先请求第一个接口得到一个结果,再把第一个结果作为参数传到第二个接口,但是第二个接口一直

    2024年02月12日
    浏览(28)
  • 关于Caused by: java.io.IOException: DerInputStream.getLength(): lengthTag=111, too big异常解决方法

    目录 前言:遇到这种问题,从字面意思来讲是因为文件太大,超过了文件的预留长度 一、再现场景: 二、排除错误: 三、解决办法:         1、第一种:修改yml文件中的证书密码:         2、第二种:在依赖中加入maven-resources-plugin组件依赖 将http请求转变为https请求,再

    2024年02月11日
    浏览(33)
  • 关于MyBatis拦截器失效问题的解决(多数据源、分页插件)

    最近做了一个备份被delete语句删除的数据的小插件,是用MyBatis的拦截器去做的。 但是发现在一个项目中会失效,没有去备份删除的数据,查看日志发现请求并没有进入到拦截器中,换句话说就是拦截器失效了。 百度了一下(哎,百度)发现说的最多的就是分页插件导致的,

    2024年02月14日
    浏览(27)
  • quarkus依赖注入之十一:拦截器高级特性上篇(属性设置和重复使用)

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇是《quarkus依赖注入》系列的第十一篇,之前的[《拦截器》]学习了拦截器的基础知识,现在咱们要更加深入的了解拦截器,掌握两种高级用法:拦截器属性和重复使用拦截器 先来回顾拦截器的基

    2024年02月13日
    浏览(23)
  • EasyExcel代码层面设置写出的Excel样式、以及拦截器策略的使用、自动列宽设置、EasyExcel默认设置详解

    虽然 EasyExcel 已经提供了一系列注解方式去设置样式。 但是如果没有实体类,或者想要更精确的去设置导出文件的 Excel 样式的时候就需要在代码层面去控制样式了。 主要步骤: 创建 Excel 对应的实体对象 创建一个 style 策略 并注册 写出 Excel 第一步是否需要创建Excel实体对象

    2024年02月13日
    浏览(29)
  • vue全家桶进阶之路46:Vue3 Axios拦截器和globalProperties全局设置

    在Vue.js 3中,使用Axios与Vue.js 2.x中类似,但是需要进行一些修改和更新,下面是Vue.js 3中Axios的定义和使用方式: 首先,你需要安装Axios和Vue.js 3.x,可以使用npm或yarn等包管理工具安装: 然后,在你的Vue.js 3应用程序中,你可以使用以下代码来导入和使用Axios: 上面的代码使用

    2023年04月20日
    浏览(39)
  • Java开发 - 拦截器初体验

    目录 前言  拦截器 什么是拦截器 拦截器和过滤器 Spring MVC的拦截器

    2023年04月17日
    浏览(26)
  • JAVA中的拦截器、过滤器

    相关解释:拦截器依赖于页面有访问controller的操作,且属于SpringMVC体系的动态拦截调用机制,是java中AOP思想的运用。 来看看源码作者的注释: 其中倒数第二段话,描述其类似于过滤器,但其特点只允许使用自定义预处理,不能处理程序本身。此处可体现AOP思想。 过滤器是

    2024年02月13日
    浏览(27)
  • 【Java Web】用拦截器的方式获取用户信息

    流程:从cookie中获取凭证,根据凭证查询用户,并在本次请求中持有用户,在视图模板上显示登录用户的信息。 1. 定义拦截器 2. 配置拦截器

    2024年02月10日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包