在拦截器中读取request参数,解决在controller中无法二次读取的问题

 更新时间:2021年10月26日 11:28:47   作者:张子茏  
这篇文章主要介绍了在拦截器中读取request参数,解决在controller中无法二次读取的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

拦截器中读取request参数,在controller中无法二次读取

新建类

package com.ouyeelbuy.mc.common.base; 
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
 
/**
 * @author robin.zhang
 * @date $
 * @description 解决在request的数据流只能读取一次的问题
 */
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 {
            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 String getBody() {
        return this.body;
    }
}

添加过滤器

package com.ouyeelbuy.mc.common.filter;
import com.ouyeelbuy.mc.common.base.RequestWrapper;
import org.springframework.stereotype.Component; 
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
 
/**
 * @author robin.zhang
 * @date $
 * @description 解决request数据流只能读取一次的问题
 */
@Component
public class RequestFilter 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);
        }
        if(requestWrapper == null) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            filterChain.doFilter(requestWrapper, servletResponse);
        }
    }
 
    @Override
    public void destroy() {
    }
}

然后注册这个过滤器到spring容器中,问题解决!

使用拦截器时,controller中不能再次获取body中的参数

报错信息:从报错信息可以看出,io流已经关闭。这是由于拦截器读取了body中的参数信息,导致controller不能再次读取。

I/O error while reading input message; nested exception is java.io.IOException: Stream closed

解决办法

在拦截器读取到body信息后,再次将内容写入

1、获取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.Charset;
 
/**
 * 保存流
 */
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body; 
    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        String sessionStream = getBodyString(request);
        body = sessionStream.getBytes(Charset.forName("UTF-8"));
    }
 
    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    public String getBodyString(final ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = cloneInputStream(request.getInputStream());
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
 
    /**
     * Description: 复制输入流</br>
     *
     * @param inputStream
     * @return</br>
     */
    public 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) {
            e.printStackTrace();
        }
        InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        return byteArrayInputStream;
    }
 
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
 
    @Override
    public ServletInputStream getInputStream() throws IOException { 
        final ByteArrayInputStream bais = new ByteArrayInputStream(body); 
        return new ServletInputStream() {
 
            @Override
            public int read() throws IOException {
                return bais.read();
            }
 
            @Override
            public boolean isFinished() {
                return false;
            }
 
            @Override
            public boolean isReady() {
                return false;
            }
 
            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
}

2、重新写入

import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
// 表示执行过滤顺序,值越小,越先执行
@Order(1)
// 配置需要过滤的地址
@WebFilter(filterName = "bodyReaderFilter", urlPatterns = "/*")
public class BodyReaderFilter implements Filter {
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
//        System.out.println("--------------过滤器初始化------------");
    }
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//        System.out.println("--------------执行过滤操作------------");
 
        // 防止流读取一次后就没有了, 所以需要将流继续写出去
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
        filterChain.doFilter(requestWrapper, servletResponse);
    }
 
    @Override
    public void destroy() {
//        System.out.println("--------------过滤器销毁------------");
    } 
}

3、注册过滤器

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
 
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
// 注册过滤器注解
@ServletComponentScan
public class WebPlatformApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebPlatformApplication.class);
    }
}

添加完成后,运行正常!

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

相关文章

  • 解决springboot中自定义JavaBean返回的json对象属性名称大写变小写问题

    解决springboot中自定义JavaBean返回的json对象属性名称大写变小写问题

    开发过程中发现查询返回的数据出现自定义的JavaBean的属性值大小写格式出现问题,导致前端无法接受到数据,目前有四种解决方法,根据大佬的经验之谈,前两种是最简单便捷的,后两种是比较通用的方法,需要的朋友可以参考下
    2023-10-10
  • Spring基于AspectJ的AOP开发案例解析

    Spring基于AspectJ的AOP开发案例解析

    这篇文章主要介绍了Spring的基于AspectJ的AOP开发,AspectJ是一个基于Java语言的AOP框架,使用AspectJ需要导入Spring AOP和AspectJ相关jar包,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • Java16 JDK安装并设置环境变量的方法步骤

    Java16 JDK安装并设置环境变量的方法步骤

    突然想起自己大学刚接触java的时候,要下载JDK和配置环境变量,那时候我上网找了很多教学,本文就详细的介绍一下Java16 JDK安装并设置环境变量,感兴趣的可以了解一下
    2021-09-09
  • springboot 配置日志 打印不出来sql的解决方法

    springboot 配置日志 打印不出来sql的解决方法

    这篇文章主要介绍了springboot 配置日志 打印不出来sql的解决方法,帮助大家更好的理解和使用springboot框架,感兴趣的朋友可以了解下
    2020-11-11
  • SpringBoot3整合SpringSecurity6快速入门示例教程

    SpringBoot3整合SpringSecurity6快速入门示例教程

    SpringSecurity 是Spring大家族中一名重要成员,是专门负责安全的框架,本文给大家介绍SpringBoot3整合SpringSecurity6快速入门示例教程,感兴趣的朋友一起看看吧
    2025-04-04
  • 给你的MyBatis-Plus装上批量插入的翅膀(推荐)

    给你的MyBatis-Plus装上批量插入的翅膀(推荐)

    这篇文章主要介绍了给你的MyBatis-Plus装上批量插入的翅膀,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • 浅谈java是如何做资源回收补救的

    浅谈java是如何做资源回收补救的

    这篇文章主要介绍了浅谈java是如何做资源回收补救的,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • Java中的自动拆装箱、基本类型的转换、包装类的缓存详解

    Java中的自动拆装箱、基本类型的转换、包装类的缓存详解

    文章详细介绍了Java中数据类型的拆装箱、自动拆箱和装箱,以及包装类的缓存机制,包括基本数据类型的容量大小、转换规则和自动类型转换等
    2024-12-12
  • Java中常用判空与判等方法详解

    Java中常用判空与判等方法详解

    java中感觉判空的一些方法太多了,感觉有点儿乱糟糟的,所以这篇文中就为大家简单总结一下在项目中常用的一些方法吧,希望对大家有所帮助
    2025-03-03
  • 存储过程创建及springboot代码调用存储过程方式

    存储过程创建及springboot代码调用存储过程方式

    文章介绍了如何在Navicat中创建存储过程,并在Spring Boot项目中调用存储过程,存储过程创建步骤包括选择函数类型、自定义函数名、添加参数等,在Spring Boot中调用存储过程时,可以通过JdbcTemplate或MyBatis等工具进行
    2024-11-11

最新评论