getReader() has already been called for this request

简介: getReader() has already been called for this request

问题现场:image.png

原因:



HttpServletRequest 的 getInputStream() 和 getReader() 都只能读取一次。


因为 我们使用@RequestBody 注解,读取body参数;而 又 写了拦截器,也需要将post请求,body数据拿出来。

由于@RequestBody 也是流的形式读取,流读了一次就没有了。


解决方案:



过滤器是优先于拦截器的, 我们写一个过滤器,在过滤器里面 把流数据 copy一份出来用,也就是复写一哈。


在拦截器上使用我们复写的流数据就行。


BodyWrapperFilter.java


import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
 * @Author: JCccc
 * @Date: 2022-6-12 10:35
 * @Description:
 */
public class BodyWrapperFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if(servletRequest instanceof HttpServletRequest) {
            requestWrapper = new CustomHttpServletRequestWrapper((HttpServletRequest) servletRequest);
        }
        if(requestWrapper == null) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            filterChain.doFilter(requestWrapper, servletResponse);
        }
    }
}


CustomHttpServletRequestWrapper.java


import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
 * @Author: JCccc
 * @Date: 2022-6-12 10:36
 * @Description:  重写一个自己的 RequestWrapper 拿出body给自己用
 */
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private byte[] body;
    public CustomHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        BufferedReader reader = request.getReader();
        try (StringWriter writer = new StringWriter()) {
            int read;
            char[] buf = new char[1024 * 8];
            while ((read = reader.read(buf)) != -1) {
                writer.write(buf, 0, read);
            }
            this.body = writer.getBuffer().toString().getBytes();
        }
    }
    public String getBody(){
        return new String(body, StandardCharsets.UTF_8);
    }
    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
            @Override
            public int read() {
                return byteArrayInputStream.read();
            }
        };
    }
    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}


WebApplicationConfig.java


import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @Author: JCccc
 * @Date: 2022-6-23 10:52
 * @Description:
 */
@Configuration
public class WebApplicationConfig {
    @Bean
    BodyWrapperFilter getBodyWrapperFilter(){
        return new BodyWrapperFilter();
    }
    @Bean("bodyWrapperFilter")
    public FilterRegistrationBean<BodyWrapperFilter> checkUserFilter(BodyWrapperFilter bodyWrapperFilter) {
        FilterRegistrationBean<BodyWrapperFilter> registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(bodyWrapperFilter);
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(1);
        registrationBean.setAsyncSupported(true);
        return registrationBean;
    }
}


然后就是在拦截器里面,如果我们想取出body,我们改成这样用:


CustomHttpServletRequestWrapper wrapper = (CustomHttpServletRequestWrapper) request;

String  nowParams = wrapper.getBody();


效果:


image.png


好的,这篇就到这。

相关文章
|
SQL Oracle Java
达梦 报错 数据类型不匹配
讲述DM数据库报错数据类型不匹配问题处理
|
NoSQL Java Redis
Spring boot整合Redis实现发布订阅(超详细)
Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收信息。微信,微博,关注系统 Redis客户端可以订阅任意数量的频道
9262 0
Spring boot整合Redis实现发布订阅(超详细)
|
SQL 存储 Oracle
6 张图带你彻底搞懂分布式事务 XA 模式
XA 协议是由 X/Open 组织提出的分布式事务处理规范,主要定义了事务管理器 TM 和局部资源管理器 RM 之间的接口。目前主流的数据库,比如 oracle、DB2 都是支持 XA 协议的。
14487 1
6 张图带你彻底搞懂分布式事务 XA 模式
|
7月前
|
Java Spring
Spring Boot配置的优先级?
在Spring Boot项目中,配置可通过配置文件和外部配置实现。支持的配置文件包括application.properties、application.yml和application.yaml,优先级依次降低。外部配置常用方式有Java系统属性(如-Dserver.port=9001)和命令行参数(如--server.port=10010),其中命令行参数优先级高于系统属性。整体优先级顺序为:命令行参数 &gt; Java系统属性 &gt; application.properties &gt; application.yml &gt; application.yaml。
1156 0
|
缓存 NoSQL Java
Spring Boot 3 整合 Spring Cache 与 Redis 缓存实战
Spring Boot 3 整合 Spring Cache 与 Redis 缓存实战
|
11月前
|
NoSQL Java Redis
springboot怎么使用Redisson
通过以上步骤,已经详细介绍了如何在Spring Boot项目中使用Redisson,包括添加依赖、配置Redisson、创建配置类以及使用Redisson实现分布式锁和分布式集合。Redisson提供了丰富的分布式数据结构和工具,可以帮助开发者更高效地实现分布式系统。通过合理使用这些工具,可以显著提高系统的性能和可靠性。
3758 34
|
10月前
|
Arthas 监控 Java
Arthas trace (方法内部调用路径,并输出方法路径上的每个节点上耗时)
Arthas trace (方法内部调用路径,并输出方法路径上的每个节点上耗时)
1559 10
|
Java 数据库连接 mybatis
mybatis 返回map
mybatis 返回map
433 0
(详细图解过程) IDEA在创建类的的时候自动生成作者信息、时间等信息
这篇文章介绍了如何在IntelliJ IDEA中设置文件和代码模板,以便在创建新类时自动生成包含作者信息、日期和时间等信息的文件头。
(详细图解过程) IDEA在创建类的的时候自动生成作者信息、时间等信息
|
SQL 数据库
SQL中CASE WHEN THEN ELSE END的用法详解
SQL中CASE WHEN THEN ELSE END的用法详解
3908 2