小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Required request body is missing ...ContentCachingRequestWrapper

 縷夢菲煙 2022-01-18

對于開發(fā)中我們在過濾器或者攔截器中往往會對請求體做驗證,往往會導(dǎo)致發(fā)生Required request body is missing的問題

例如

那么,在controller就無法取到這個的值而是直接拋出

有的博客會提出使用ConContentCachingRequestWrapper包裝一下ServletRequest來解決,但是實際使用,對于controller參數(shù)中使用@RequestBody,仍舊會導(dǎo)致相同的問題。

類似于

而他的緩存(caching)性其實表現(xiàn)在這里,舉個例子

這里getContentAsByteArray就是可以重復(fù)讀取的一個方法,其底層就是ByteArrayOutputStream

1596895668128

既然它可以被重復(fù)讀取那么為什么又會因為無法重復(fù)讀取而拋出異常呢?

為什么ConContentCachingRequestWrapper無法解決的重復(fù)讀取問題

點(diǎn)我直接看結(jié)論

我們先故意觸發(fā)這個異常,看看異常堆棧信息

再轉(zhuǎn)到對應(yīng)方法

對于我們這個需要反序列化的參數(shù),含有RequestBody注解,且使用required的默認(rèn)值true,且不為optional,所以這個判斷函數(shù)為true,所以拋出這個異常的原因在于arg為null,進(jìn)而問題出在readWithMessageConverters方法上。

那么我們再去尋找什么情況下這個方法會返回null(其實不是這里返回的null,這里是啟發(fā)我向上找的原因)

我們再向上尋找body的賦值,同時發(fā)現(xiàn)上面也有個一個對messag.hasBody()的判定

那么我們就把斷點(diǎn)放到這里(有注釋的AbstractMessageConverterMethodArgumentResolver類202行),再此執(zhí)行

發(fā)現(xiàn)其實它比較的是內(nèi)部的body是否為空,我們再來看這個EmptyBodyCheckingHttpInputMessage類到底在哪里初始化的body這個變量

原來是在構(gòu)造函數(shù)里面,我們再在藍(lán)色高亮處打個斷點(diǎn)再重新試試

結(jié)合idea提示的類型信息和源碼,也就說如果body不為空,那么其中含有的inputstream類就要支持(mark,reset)或者還未讀取完畢。

我們再回看ContentCachingRequestWrapper這個類中的ContentCachingInputStream類,首先這個時候因為我們故意在攔截器消費(fèi)了這個流,所以我們要看看它支不支持(mark,reset)功能

所以說不支持

那么我們再看else分支的這個PushbackInputStream和他的read方法到底何方神圣

因為單參數(shù)初始化的后的pos =1 buf數(shù)組長度 =1,即返回值為super.read()的返回值

即傳入的那個inputStream調(diào)用read()方法

結(jié)論

你看問題就出在這里。還是調(diào)用的ServletInputStream的read,因為直接原請求流被我們消費(fèi)了,所以返回值為-1

再走到了else中進(jìn)行處理空body

在這個方法中返回了null(因為第一個參數(shù)為null),這就是為什么這個方法返回為null的真正原因

進(jìn)而我們上面提到的這個if為true的,也就因此拋出了這個我們熟悉的

Required request body is missing異常提示

歸根結(jié)底是因為ContentCachingRequestWrapper的內(nèi)部類 ContentCachingInputStream的read方法還是由ServletInputStream去執(zhí)行read方法的

解決方案

我來提供一個簡單的解決方法

我們先來復(fù)習(xí)一下我們需要什么樣的InputStream?支持reset,mark

那么jdk有沒有這樣一個呢?有!ByteArrayInputStream,這個是個實現(xiàn)InputStream的假裝成流的字符數(shù)組緩存。

設(shè)計思路如下,由這個包裝類先行消費(fèi)輸入流做成比特數(shù)組儲存起來,通過getInputStream提供一個

ServletInputStream的實現(xiàn)類用于代理ByteArrayInputStream進(jìn)行操作

ByteArrayInputStream只是保留了一個引用,同時這個body的字符數(shù)組是只讀的,也不用擔(dān)心線程安全問題,更不用擔(dān)心ByteArrayInputStream的關(guān)閉問題(畢竟不是真正的流)

public class RepeatableRequestWrapper extends HttpServletRequestWrapper {
    private byte[] body;
    private Charset charset;
    @SneakyThrows
    public RepeatableRequestWrapper(HttpServletRequest request, Charset charset) {
        super(request);
        body = request.getInputStream().readAllBytes();
        this.charset = charset;
    }

    public RepeatableRequestWrapper(HttpServletRequest request) {
        this(request,StandardCharsets.UTF_8);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new RepeatableInputStream();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream(),charset));
    }

    private class RepeatableInputStream extends ServletInputStream{
        private ByteArrayInputStream byteArrayInputStream;

        @Override
        public synchronized void reset() throws IOException {
            byteArrayInputStream.reset();
        }

        @Override
        public synchronized void mark(int readlimit) {
            byteArrayInputStream.mark(readlimit);
        }

        public RepeatableInputStream() {
            byteArrayInputStream = new ByteArrayInputStream(body);
        }

        @Override
        public boolean isFinished() {
            return byteArrayInputStream.available() == 0;
        }

        @Override
        public boolean isReady() {
            return true;
        }

        @Override
        public void setReadListener(ReadListener readListener) {
            throw new UnsupportedOperationException("不支持監(jiān)聽");
        }

        @Override
        public int read() throws IOException {
            return byteArrayInputStream.read();
        }

        @Override
        public boolean markSupported() {
            return byteArrayInputStream.markSupported();
        }
    }

}

復(fù)制代碼

我們再來請求一次

舒服了,這次可以了

分類

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多