最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot 解決過(guò)濾器 HttpServletRequest body IO流只能讀取一次的問(wèn)題

 更新時(shí)間:2026年05月19日 09:43:25   作者:java葉新東  
本文主要介紹了在過(guò)濾器中打印請(qǐng)求body參數(shù)的難點(diǎn)及其原因,并提供了一種解決方案:使用包裝類(lèi)緩存流數(shù)據(jù),這樣可以在過(guò)濾器中打印body內(nèi)容的同時(shí),不影響控制器獲取body參數(shù),感興趣的可以了解一下

問(wèn)題

老手都知道過(guò)濾器的作用,但是如果想要打印請(qǐng)求的參數(shù)就有點(diǎn)難度了, url 上拼接的參數(shù)可以輕而易舉的得到,調(diào)用 request.getParameterMap() 方法就可以拿到;

但是要拿body 體就沒(méi)這么簡(jiǎn)單了,request 里面有個(gè) getInputStream(),這個(gè)流 是一次性的, 如果你在這里讀出來(lái),那么在控制層就拿不到body參數(shù)了;

錯(cuò)誤方式:直接讀取原始流打?。▽?dǎo)致控制器無(wú)法獲?。?/h3>

如果在過(guò)濾器中直接調(diào)用原始請(qǐng)求的 getInputStream() 或 getReader() 打印,會(huì)直接消耗流,導(dǎo)致控制器后續(xù)讀取時(shí)無(wú)數(shù)據(jù):

// 錯(cuò)誤示例:直接讀取原始流,導(dǎo)致流被消耗

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    // 直接讀取原始流并打?。ㄥe(cuò)誤?。?
    String body = IOUtils.toString(httpRequest.getInputStream(), StandardCharsets.UTF_8);
    System.out.println("Filter打印body:" + body);
    // 此時(shí)流已被消耗,控制器無(wú)法讀取
    chain.doFilter(request, response); 
}

方案

問(wèn)題本質(zhì):輸入流的 “一次性” 特性
ServletRequest 的輸入流(getInputStream()/getReader())是單向流動(dòng)的字節(jié)流,默認(rèn)情況下,一旦讀取(如在過(guò)濾器中打?。?,流的指針會(huì)移動(dòng)到末尾,后續(xù)(如控制器)再讀取時(shí)就會(huì)獲取到空數(shù)據(jù)。
因此,問(wèn)題的核心不是 “是否在過(guò)濾器中打印”,而是 “打印時(shí)是否消耗了原始流且未緩存”。

正確方式:通過(guò)包裝類(lèi)緩存后打?。刂破骺烧+@?。?/h3>

如果先通過(guò) HttpServletRequestWrapper 緩存流數(shù)據(jù),再在過(guò)濾器中讀取緩存的數(shù)據(jù)打印,原始流不會(huì)被消耗,控制器可通過(guò)包裝類(lèi)的緩存讀??;

過(guò)濾器內(nèi)容

@Component
@0rder(o)
@Slf4j
public class WebFilter extends OncePerRequestFilter {

@0verride
protected void dofilterInternal(HttpservletRequest request, HttpservletResponse response, Filterchain filterchain)throws servletException, I0Exception{
// 正確示例:基于包裝類(lèi)的緩存打印,不消耗原始流
    if (request instanceof HttpServletRequest) {
        // 先包裝請(qǐng)求,緩存body
        CachedServletRequestWrapper wrappedRequest = new CachedServletRequestWrapper((HttpServletRequest) request);
        // 從包裝類(lèi)的緩存中讀取并打?。ú挥绊懺剂鳎?
        String body = new String(wrappedRequest.getCachedBody(), StandardCharsets.UTF_8);
        System.out.println("Filter打印body:" + body);
        // 傳遞包裝后的請(qǐng)求,控制器可從緩存中讀取
        chain.doFilter(wrappedRequest, response);
    } else {
        chain.doFilter(request, response);
    }
}

CachedServletRequestWrapper.java

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

/**
 * 緩存請(qǐng)求體的包裝類(lèi),解決ServletRequest輸入流只能讀取一次的問(wèn)題
 */
public class CachedServletRequestWrapper extends HttpServletRequestWrapper {

    // 緩存的請(qǐng)求體字節(jié)數(shù)組
    private final byte[] cachedBody;

    /**
     * 構(gòu)造方法:讀取原始請(qǐng)求的輸入流并緩存
     */
    public CachedServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.cachedBody = readRequestBody(request);
    }

    /**
     * 讀取原始請(qǐng)求的輸入流并轉(zhuǎn)換為字節(jié)數(shù)組
     */
    private byte[] readRequestBody(HttpServletRequest request) throws IOException {
        // GET/DELETE等方法通常沒(méi)有請(qǐng)求體,直接返回空數(shù)組
        String method = request.getMethod();
        if ("GET".equalsIgnoreCase(method) || "DELETE".equalsIgnoreCase(method)) {
            return new byte[0];
        }

        // 讀取輸入流并緩存
        try (InputStream inputStream = request.getInputStream();
             ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, length);
            }
            return outputStream.toByteArray();
        }
    }

    /**
     * 重寫(xiě)getInputStream(),返回基于緩存的輸入流
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cachedBody);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }

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

            @Override
            public boolean isReady() {
                return true; // 同步讀取時(shí)始終就緒
            }

            @Override
            public void setReadListener(ReadListener listener) {
                // 異步場(chǎng)景可忽略,同步場(chǎng)景無(wú)需實(shí)現(xiàn)
            }
        };
    }

    /**
     * 重寫(xiě)getReader(),基于緩存的輸入流構(gòu)建字符讀取器
     */
    @Override
    public java.io.BufferedReader getReader() throws IOException {
        return new java.io.BufferedReader(
            new java.io.InputStreamReader(getInputStream(), StandardCharsets.UTF_8)
        );
    }

    /**
     * 提供獲取緩存的請(qǐng)求體字節(jié)數(shù)組的方法(方便過(guò)濾器/攔截器直接使用)
     */
    public byte[] getCachedBody() {
        return cachedBody;
    }

    /**
     * 提供獲取緩存的請(qǐng)求體字符串的方法(簡(jiǎn)化使用)
     */
    public String getCachedBodyAsString() {
        return new String(cachedBody, StandardCharsets.UTF_8);
    }
}

可以看到,正常打印了body數(shù)據(jù)

到此這篇關(guān)于springboot 解決過(guò)濾器 HttpServletRequest body IO流只能讀取一次的問(wèn)題的文章就介紹到這了,更多相關(guān)springboot HttpServletRequest body IO讀取一次內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

鄄城县| 甘孜| 鹤岗市| 安康市| 会泽县| 岳池县| 五寨县| 垣曲县| 花垣县| 通山县| 宁明县| 镇巴县| 北安市| 黄梅县| 邳州市| 崇义县| 临朐县| 常德市| 汝州市| 永新县| 射阳县| 山东| 泾川县| 沙河市| 轮台县| 仲巴县| 松桃| 桐梓县| 湖口县| 彭水| 临夏县| 揭东县| 寻甸| 潜江市| 南皮县| 秀山| 钦州市| 民丰县| 依兰县| 北海市| 长武县|