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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM