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

springboot 解決過濾器 HttpServletRequest body IO流只能讀取一次的問題

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

問題

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

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

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

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

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

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

方案

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

正確方式:通過包裝類緩存后打?。刂破骺烧+@?。?/h3>

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

過濾器內(nèi)容

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

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

/**
 * 緩存請求體的包裝類,解決ServletRequest輸入流只能讀取一次的問題
 */
public class CachedServletRequestWrapper extends HttpServletRequestWrapper {

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

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

    /**
     * 讀取原始請求的輸入流并轉(zhuǎn)換為字節(jié)數(shù)組
     */
    private byte[] readRequestBody(HttpServletRequest request) throws IOException {
        // GET/DELETE等方法通常沒有請求體,直接返回空數(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();
        }
    }

    /**
     * 重寫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) {
                // 異步場景可忽略,同步場景無需實(shí)現(xiàn)
            }
        };
    }

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

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

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

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

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

相關(guān)文章

最新評論

历史| 康平县| 长乐市| 太仓市| 武城县| 枣庄市| 东台市| 泾阳县| 秦安县| 聂荣县| 徐州市| 巧家县| 望城县| 会昌县| 夏邑县| 吴桥县| 尉氏县| 汽车| 安岳县| 乌什县| 永兴县| 剑川县| 滕州市| 武宁县| 孝昌县| 汶川县| 鹤壁市| 连云港市| 安顺市| 长垣县| 左权县| 台南市| 托克逊县| 云梦县| 清苑县| 淮安市| 砚山县| 龙川县| 永康市| 谢通门县| 大姚县|