zoukankan      html  css  js  c++  java
  • HttpServletRequest.getInputStream() 多次获取post的数据

    HttpServletRequest.getInputStream() 多次获取post的数据

    在实际的开发过程中,我们会在Filter或者AOP中读取body数据进行数据校验,
    GET方法获取参数比较简单。可以直接对HttpServletRequest类使用getQueryString和getParameterMap获取到,但是对于POST方法,可使用如下方法从request中获取body参数:

     private String getPostData(HttpServletRequest request) throws IOException {
        InputStream in = request.getInputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(in, Charset.forName("UTF-8")));
        StringBuffer sb = new StringBuffer("");
        String temp;
        while ((temp = br.readLine()) != null) {
            sb.append(temp);
        }
        if (in != null) {
            in.close();
        }
        if (br != null) {
            br.close();
        }
        return sb.toString();
    }
    

    这样子虽然能够获取到post的数据,但是系统会报一个异常:

    java.lang.IllegalStateException: getInputStream() has already been called ...
    

    原来:

    • 一个InputStream对象在被读取完成后,将无法被再次读取,始终返回-1;
    • InputStream并没有实现reset方法(可以重置首次读取的位置),无法实现重置操作;

    因此,当自己写的Filter中调用了一次getInputStream()后,后面再调用getInputStream()读取的数据都为空,所以才报IllegalStateException错误。

    解决办法

    1、新建一个 MyRequestWrapper类 对父类的 HttpServletRequestWrappergetInputStream()方法进行重写,代码如下:

    import com.tusdao.log.util.RequestParamAware;
    
    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    
    public class MyRequestWrapper extends HttpServletRequestWrapper {
        private final String body;
        public MyRequestWrapper(HttpServletRequest request) throws IOException {
            super(request);
            StringBuilder sb = new StringBuilder();
            String line;
            BufferedReader reader = request.getReader();
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            body = sb.toString();
            request.setAttribute("body",body); //将post的body数据放入缓存,这样子在后面就能够随时取用
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
            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) {
                }
            };
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(this.getInputStream()));
        }
    }
    
    

    2、在拦截器中传入Wrapper对象:

    @Order(1)
    @Component
    @Slf4j
    @WebFilter(filterName="logFilter", urlPatterns="/*")
    public class LogFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            log.info("filter before");
            // 转换成自己覆写的类
            MyRequestWrapper req = new MyRequestWrapper((HttpServletRequest)request);
            log.info("获取到post信息为:{}", RequestParamAware.extractPostBody(request));
            // 如果没有覆写HttpServletRequestWrapper
            // doFilter(request, response)之后 再用到body
            // 会抛出类似错误 Cannot call getInputStream(), getReader() already called
          	// 注意doFilter这里传进去的参数req 而不是request !!!!
            chain.doFilter(req, response);
            log.info("filter after");
        }
    
        @Override
        public void destroy() {
    
        }
    }
    

    这样,位于后面的controller就可以拥有唯一一次调用HttpServletRequest.getInputStream()的机会了。并且在后序的业务中可以通过getAttribute获取到post的数值。

  • 相关阅读:
    MQTT的编译和安装(mosquitto)
    四、固件分析-固件逆向
    三、固件分析-固件提取
    二、硬件分析-电路分析
    一、硬件分析
    gdb调试
    AES加密中遇到的坑
    多线程中快速定位段错误位置
    理想的数据加密流程
    Centos进入单用户修改root密码
  • 原文地址:https://www.cnblogs.com/xwxz/p/14061714.html
Copyright © 2011-2022 走看看