使HttpServletRequest中getReader()和getInputStream()可重复使用

简介: 自定义HttpServletRequest使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加入即可

目录
相关文章
|
8月前
|
XML 数据格式
HttpServletRequest的介绍和方法以及代码实战
HttpServletRequest的介绍和方法以及代码实战
178 0
|
XML 缓存 Java
HttpServletRequest 对象
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。 其他的具体方法就不详细描述了,具体方法上的使用看API,或者网上查查,有很多的。这里就介绍一下 请求方式 和 获取参数 的问题
Servlet学习(六):ServletContext对象的作用
Servlet学习(六):ServletContext对象的作用
Servlet学习(六):ServletContext对象的作用
|
应用服务中间件
HttpServletRequest 类
HttpServletRequest 类
HttpServletRequest 类
Servlet—HttpServletRequest与HttpServletResponse对象常用方法
Servlet—HttpServletRequest与HttpServletResponse对象常用方法
Servlet—HttpServletRequest与HttpServletResponse对象常用方法
|
前端开发 应用服务中间件 程序员
HttpServletRequest接口
HttpServletRequest接口
115 0
HttpServletRequest接口
HttpServletRequest对象的生命周期监听器
HttpServletRequest对象的生命周期监听器的个人理解
HttpServletRequest对象的属性操作监听器
HttpServletRequest对象的属性操作监听器三种常见方法
|
存储 数据安全/隐私保护
HttpServletRequest:增删改查cookie
HttpServletRequest:增删改查cooki
651 0
HttpServletRequest:增删改查cookie