【java web】解决流读完一次就不能再次获取body数据的问题

【Servlet】利用 HttpServletRequestWrapper 实现对request body的二次读取,可用于记录日志         最近项目上有一个需求,在api上收到的请求,需要在springmvc转化成实体参数之前把request body读取出来记录日志。         在通常的响应程上,使用了request.getInputStream()之后,就会失效,即这个request body只能读取一次,这也是本身的特性所致(当然,还有一种特殊的——推回输入PushbackInputStre... 阅读详情

问题来自我工作业务上的需求:前端请求时需要将json用RSA算法加密,数据经过后端过滤器进行自动解密,这样做的好处是以后不需要在每一个方法里都手动解密一次,增加代码的简洁性、可维护性。

但这样一来便会面临一个问题:http的request请求的输入流在过滤器中就已经被读取了(因为需要读取并解密request body 里被前端加密了的json数据),流只能被读取一次,这样一来数据便传不进controller里,导致接下来的业务无法进行。

于是我上网找了一些资料并成功解决了这个问题,基本思路是封装原生的HttpServletRequest请求,将其输入流里的数据保存在字节数组里,最后重写getInputStream方法,使其之后每次读取数据都是从字节数组里读取的。

第一步:写一个Request包装类BodyReaderHttpServletRequestWrapper

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
    }

    @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) {

            }

        };
    }

    public void setInputStream(byte[] body){
        this.body = body;
    }

里面涉及一个HttpHelper类,顺便也贴出来

public class HttpHelper {

    /**
     * 获取请求Body
     * @param request
     * @return
     */
    public static String getBodyString(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = 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();
    }
    
}

第二步:编写包装Filter

这个filter类可以将经过的原生Request请求自动包装成BodyReaderHttpServletRequestWrapper

/**
 * desc : 用于包装原生request, 解决流读完一次就不能再次获取body数据的问题
 * Created by Lon on 2018/3/9.
 */
public class RequestWrapperFilter implements Filter{

    private static final Logger LOGGER = LoggerFactory.getLogger(RequestWrapperFilter.class);

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

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if(request instanceof HttpServletRequest) {
            requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
        }
        if(null == requestWrapper) {
            LOGGER.error("包装request失败!将返回原来的request");
            chain.doFilter(request, response);
        } else {
            LOGGER.info("包装request成功");
            chain.doFilter(requestWrapper, response);
        }

    }

    @Override
    public void destroy() {

    }
}

第三步:在web.xml上配置过滤器

  <filter>
    <filter-name>RequestWrapperFilter</filter-name>
    <filter-class>com.kx.security.filter.RequestWrapperFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>RequestWrapperFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

这里要注意的是这个包装过滤器可能要写在web.xml配置文件里某些过滤器的前面,比如解密过滤器,否则被解密过滤器先读取流的话,包装过滤器就读取不了流了。

至此一个简单的解决方法就完成啦


Http请求解决body一旦被读取了就无法二次读取情况 大家好我是yangerkong! 相信大家在工作当中,经常会遇到需要处理http请求及响应body的场景,这里最大的问题应该就是body以但被读取就无法二次读取了。 针对这个问题,下面是我在项目中的处理方式 我们编写一个过滤器,这样就可以重写body了 package com.interceptor; import java.io.IOException; import java.util.Arrays; import java.util.List; import javax.servlet 阅读详情

相关推荐

(遇到问题)(解决)word敲入的公式转化为mathtype

https://www.mathtype.cn/wenti/wordgongshi-zhuanhuan.html 现在越来越多的人在文档中编辑公式的时候会选择MathType来编辑,因为它包含有众多的数学符号和模板,编辑公式时非常方便。但是也有一些人在编辑公式时没有使用MathType公式编辑器,直接使用Word中自带的公式编辑器来编辑的。当我们需要要共享一份文件的时候,这种用自带公式编辑器编辑出来的文件,在我们安装有MathType公式编辑器的电脑里打开时,双击公式没有反应,是无法对它们进行修改的.

计算机视觉 1万+

阿昌教你自定义拦截器&自定义参数解析器&自定义包装HttpServletRequest

前言 这次也是依然在学习开源项目Tduck-填鸭收集器的时,阿昌在研究这项目是如何进行安全校验的,我一开始在项目里面查Shiro/SpringSecurity,我以为他使用了市面主的安全框架,但是发现,他根本没有使用,而是自定义了一系列的 拦截器&过滤器 来实现安全的校验。 比如,通过自定义注解来决定这个资源是否需要用户登录才能够访问。 在项目中我发现的自定义注解有三个 @Login @LoginUser @NoRepeatSubmit 在开始前,放上SpringMVC的执行程 镇场:

阿昌爱Java 1621

Springboot Filter 多过滤器的使用

前言 在springboot配置过滤实现方案有两种, 一种是基于serlvet 的注解 @WebFilter 进行配置,一种是使用Springboot提供的 FilterRegistrationBean注册自定义过滤器。 该篇使用的方案是后者,因为按照我以前使用的记忆里,这种方式可以避免一些偶然出现的小问题,如:过滤器没生效;生效后url匹配不生效等。 正文 在开始敲代码前,先从上帝视角看看我们这次实践案例,做了些什么: BodyReaderHttpServletRequestWr..

默默不代表沉默 4271

关于自定义 HttpServletRequestWrapper 后续读不到参数的问题

关于自定义 HttpServletRequestWrapper 后续读不到参数的问题

weixin_38989369的博客 2892

SpringCloud-Gateway获取body参数,解决只能获取一次问题,终极解决方案

这几天有个对SpringCloud-Gateway网关请求参数做安全校验的需求,网上多数教程不适用于SpringBoot2.1+版本。我在SpringCloud gateway Github中的Issues找到了答案,放上连接:作者采纳的答案。 具体代码如下: @Componentpublic class CacheBodyGatewayFilter implements Ordered, Globa...

NameLoadingFailed的博客 5763

Filter中获取请求body再次读取问题

工作需要,要将请求和响应做一些处理,写一个filter拦截请求,拦截request中body内容后,字符关闭, controller取到的请求体内容为空。 从Request中获取输入,InputStream只能被读取一次解决方案:给request添加一个包装类BodyWrapper,继承HttpServletRequestWrapper, 先从request中取输入读取中的数据,然后...

fayeyiwang的专栏 4069

java 读取request_完美解决request请求只能读取一次问题

解决request请求只能读取一次问题实际开发碰到的问题解决request请求中的数据二次或多次使用问题实际开发碰到的问题springboot项目中,为了防止sql注入,采用Filter拦截器对所有请求中的json数据进行校验,请求数据问题则继续向下执行,在后边的代码中应用到请求参数值时,发现request中的json数据为空;除上边描述的情况,尝试过两次从request中获取json数...

weixin_39520204的博客 2419

java 获取post body_java通过HttpServletRequest获取post请求中的body内容

java web应用中,我们如何获取post请求body中的内容?以及需要注意的问题。通常利用request获取参数可以直接通过req.getParameter(name)的方式获取url上面或者ajax data提交上来的参数。但是body是没有名字的,无法通过参数名字这种方式获取。这时候需要用到io的方式来获取body中的内容。这里先贴出一段代码:package com.lenovo.se...

weixin_39637571的博客 2449

Java Web打印request body

默认的ServletInputStream不支持reset,所以默认的只能读取一次,但是如果我们要打印body就需要读取inputStream,这会导致Spring无法读取body体,所以通过HttpServletRequestWrapper包装重新定义getInputStream(),让输入从我们读出来的body体中读取数据。 @Slf4j public class RequestWrapp...

当个码农写bug 3379

HttpSevletRequest Body信息不能被多次读取问题

Java Web 开发中,HTTP 请求体是客户端向服务器发送数据的主要载体,例如表单提交、JSON 数据等。当服务器收到请求后,通常通过类获取请求体的内容。然而,HTTP 请求体通常只能被读取一次。这是因为请求体使用输入()进行读取,一旦读取完毕,的位置就会到达的末尾,无法再读取。因此,在开发中,如果不小心读取请求一次,后续的代码将无法访问请求数据,从而导致数据丢失或者异常。

梦想家的跋涉 1401

Java项目里解决request只能获取一次问题

在处理 时,数据会在以下情况下被消耗: 读取请求体: 当你从 读取输入()或读取器()来获取请求的内容时,会被消耗。这通常发生在处理 POST 或 PUT 请求的情况下,这些请求类型常用于传输数据。例如,使用 或 会读取并消耗数据。框架级别的自动读取: 在某些情况下,如果你使用的是像 Spring MVC 这样的框架,框架本身可能会在处理请求时自动读取输入。例如,当你在控制器方法中使用 注解时,Spring 会自动读取请求体,并将其转换为相应的对象,这同样会消耗。某些类型的过滤器

weixin_36994098的博客 661

HttpServletRequestWrapper拦截body解决编码问题

HttpServletRequestWrapper拦截body解决编码问题 这个话题来自于做互联网接口时对数据签名引发的问题数据签名主要是为了验证数据的完整性,同时起到身份验证的作用。 post请求body体里的内容,是以的形式存在的,不能多次读取,做签名验证时拦截器要先取读取数据做校验,导致后面controller无法再次读取数据。 1、自定义一个HttpServletRequestWrapper的子类,用于对request进行包装 package com.mrx.common.wrappe

慕容雪_的博客 1960

http请求必须在body中传递参数值_Java Web 中对 ServletRequest 的一些非常规操作解决方案...

1. 前言ServletRequest 是我们搞 Java Web 经常接触的 Servlet Api 。有些时候我们要经常对其进行一些操作。这里列举一些经常的难点操作。2. 提取 body 中的数据前后端交互我们会在 body 中传递数据。我们如何从 body 中提取数据。通常我们会通过 IO 操作:/*** obtain request body** @param request the Se...

weixin_39808953的博客 633

HttpServletRequestWrapper这个类有什么作用?

BodyReaderHttpServletRequestWrapper这个类有什么作用?

m0_72926194的博客 656

SpringCloud-Gateway自定义过滤器解决body不能重复读的问题

网上的代码千奇百怪,这个是我测试后整理的。当前端请求不含body时,也能兼容。

YuChen 3248

解决filter拦截request中body内容后,字符关闭,无法传到controller的问题

原文查看:http://blog.csdn.net/pyxly1314/article/details/51802652 改编后: BodyReaderHttpServletRequestWrapper类: import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOExce

不会玩狼人杀的程序猿 1万+

spring 处理request.getInputStream()输入只能读取一次问题

一般我们会在InterceptorAdapter拦截器中对请求进行验证 正常普通接口请求,request.getParameter()可以获取,能多次读取 如果我们的接口是用@RequestBody来接受数据,那么我们在拦截器中 需要读取request的输入 ,因为 ServletRequest中getReader()和getInputStream()只能调用一次 这样就会导...

weixin_42981419的博客 6686

拦截器与过滤器(二)拦截器集成与使用

业务中,我们经常需要自定义拦截器来记录用户行为日志,普通的参数可以从request的getParameterMap中获取,而@RequestBody的参数需要从request的InputStream中获取,但是InputStream只能读取一次,如果拦截器读取了参数,controler层就读取不到参数了,所以这类参数需要单独获取,可以把request封装一下,copy一份requet,一个用于在拦...

w_t_y_y的博客 8323

SpringBoot 配置获取request中body的json格式参数

获取GET、POST请求中的参数 获取POST请求中的JSON格式的参数

Duktig的博客 8849

DISM进行系统修复,进度条卡在63.2%的解决方法

一时用不了软件,安装修复的时候也出现问题 DISM一直卡在63.2%,等了好久时间,直接强制退出寻找最终的解决方案

码农研究僧的博客 1万+

土木工程毕业设计——【6层】3618.06平米六层框架结构住宅楼施组、工程量计算、清单计价(附横道图 ).zip

土木工程毕业设计——【6层】3618.06平米六层框架结构住宅楼施组、工程量计算、清单计价(附横道图 ).zip

上一篇: 【HTTP】http协议笔记
下一篇: 【学习Spring原理】之零:前言
cwjokaka
博客等级 码龄13年 39粉丝 123原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值