zoukankan      html  css  js  c++  java
  • 使HttpServletRequest中getReader()和getInputStream()可重复使用

    一、 背景

    有时候我们的请求是post,但我们又要对参数签名,这个时候我们需要获取到body的信息,但是当我们使用*HttpServletRequestgetReader()getInputStream()获取参数后,后面不管是框架还是自己想再次获取body已经没办法获取。当然也有一些其他的场景,可能需要多次获取的情况。

    可能抛出类似以下的异常

    java.lang.IllegalStateException: getReader() has already been called for this request
    

    二、spring中的ContentCachingRequestWrapper

    spring中的ContentCachingRequestWrapper提供了getContentAsByteArray()方法用来多次读取body。
    getContentAsByteArray()消费了InputStream来缓存请求体。导致该方法不能多次使用getReader()getInputStream()。所以该方法并不通用。

    三、 自定义扩展

    1. 扩展HttpServletRequest

    创建一个自定义实现HttpServletRequest的类,步骤如下

    1.1. 创建一个自定义类

    需要继承HttpServletRequestWrapper
    并且写一个构造函数来缓存body数据

        public class CustomHttpServletRequest extends HttpServletRequestWrapper {
    
        private byte[] cachedBody;
    
        public CustomHttpServletRequest(HttpServletRequest request) throws IOException {
            super(request);
            InputStream is = request.getInputStream();
            this.cachedBody = StreamUtils.copyToByteArray(is);
        }
    }
    
    

    1.2. 重写getReader()

    @Override
    public BufferedReader getReader() throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
        return new BufferedReader(new InputStreamReader(byteArrayInputStream));
    }
    

    1.3. 重写getInputStream()

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new CachedBodyServletInputStream(this.cachedBody);
    }
    

    CachedBodyServletInputStream参考下面步骤

    2. 实现ServletInputStream

    创建一个继承了ServletInputStream的类

    public class CachedBodyServletInputStream extends ServletInputStream {
        private InputStream cachedBodyInputStream;
    
        public CachedBodyServletInputStream(byte[] cachedBody) {
            this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody);
        }
    
       
        @Override
        public boolean isFinished() {
            try {
                return cachedBodyInputStream.available() == 0;
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return false;
        }
    
       
        @Override
        public boolean isReady() {
            return true;
        }
    
        @Override
        public void setReadListener(ReadListener readListener) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public int read() throws IOException {
            return cachedBodyInputStream.read();
        }
    }
    

    3. 创建一个Filter加入到容器中

    既然要加入到容器中,可以创建一个Filter,然后加入配置
    我们可以简单的继承OncePerRequestFilter然后实现下面方法即可。

    @Override
        protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
            CustomHttpServletRequest customHttpServletRequest =
                    new CustomHttpServletRequest(httpServletRequest);
            filterChain.doFilter(customHttpServletRequest, httpServletResponse);
        }
    

    然后添加该Filter加入即可

    由于本人才疏学浅,若有错误遗漏之处,望指出,以免误导其他同学。
  • 相关阅读:
    PHP的文件下载
    ajax异步请求分页显示
    Linux的启动过程
    搭建nginx反向代理用做内网域名转发
    intellij idea 修改背景保护色&&修改字体&&快捷键大全
    IDEA入门级使用教程-
    http://blog.csdn.net/baidu_31657889/article/details/52315902
    JVM——Java虚拟机架构
    MySQL远程连接ERROR 2003 (HY000):Can't connect to MySQL server on'XXXXX'(111) 的问题
    windows上 nginx 配置代理服务,配置多域名,以及最简单实现跨域配置
  • 原文地址:https://www.cnblogs.com/daochang/p/15525132.html
Copyright © 2011-2022 走看看