使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加入即可

上一篇:开源大数据专场——阿里云峰会(上海)开发者大会PPT下载


下一篇:9月10日 Spark 社区直播【利用持久内存提速Spark】