溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何解決SpringBoot攔截器讀取流后不能再讀取的問題

發(fā)布時(shí)間:2021-10-26 13:42:17 來源:億速云 閱讀:231 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“如何解決SpringBoot攔截器讀取流后不能再讀取的問題”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“如何解決SpringBoot攔截器讀取流后不能再讀取的問題”吧!

目錄
  • 解決這個(gè)問題我能想到兩種方式

    • 第一種方法

    • 第二種方法

在SpringBoot的攔截器中通過流 ( request.getInputStream() ) 的方式讀取body中傳來的數(shù)據(jù)會(huì)導(dǎo)致controller接收不到值。

這個(gè)問題其實(shí)就是一個(gè)流讀取的問題,眾所周知在Java中input流只能讀取一次,主要原因是通標(biāo)記的方法來判斷流是否讀取完畢(讀取位 -1就是流讀取完畢)

解決這個(gè)問題我能想到兩種方式

1.通過修改標(biāo)記的方式 ( inputstream.markSupported() 方法可以判斷這個(gè)流是否支持 mark 和 reset 方法。他們分別是標(biāo)記 和 重新定位流。)

2.將流賦值給一個(gè) byte[] 數(shù)組,或者其他變量保存起來。下載讀取流時(shí)就調(diào)用這個(gè)數(shù)組就行。

第一種方法

再回到問題上來我們可以先使用第一種方法判斷 requet 中的inputStream 是否支持標(biāo)記和重新定位。因?yàn)檫@種方式實(shí)現(xiàn)起來比較簡單。無需考慮太多。

@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
         boolean b = request.getInputStream().markSupported();
         System.out.println(b);
    }
// 輸出結(jié)果為 false

上述代碼會(huì)返回一個(gè) false 那么很明顯,request 中的 input 流是不支持標(biāo)記和重新定位的。

第二種方法

我們?cè)倏紤]第二種方法,我們需要一個(gè)變量保存這個(gè)流。并且還要保證再過濾器中和controller中都要拿到這個(gè)變量。直接定義一個(gè)全局變量獲取修改傳值方式,都是可以的。全局變量這種方式我就不演示了。

下面是改變傳值方式的 demo

  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        ServletInputStream inputStream1 = request.getInputStream();
        // 各種對(duì) inputStream1 處理的操作...
        Object obejct = inputStream1;
        request.setAttribute("Params",obejct);
}

這樣就可以再controller那邊就可以直接獲取 Attribute 中的值。

但是!這樣有很大的局限性,例如: 我已經(jīng)寫好了大多數(shù)的controller方法體。這時(shí)再改用這種方式傳值。對(duì)于開發(fā)人員是一種莫大的痛苦 -_- 于是通過不屑的百度查詢到另一種方法 一一一 改寫HttpServletRequestWrapper方法。為什么是這個(gè)方法?因?yàn)樗麑?shí)現(xiàn)了 HttpServletRequest 這個(gè)接口。也是我們攔截器接收的 request 要求的類型。首先我們先看一張圖。** 注意 filter 和 inteceptor 中間。 **

如何解決SpringBoot攔截器讀取流后不能再讀取的問題

通過上方這個(gè)圖我們可以知道,在 Filter 和 Inteceptor 中間有一層Servlet。而Servlet就是提交request的地方。所以我們要重寫HttpServletRequest方法只能在Servlet之前。也就是filter 中。下面就是直接上代碼了

1.重寫 HttpServletRequest

package com.xqw.kyg.util;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.springframework.util.StreamUtils;
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper{
    private byte[] requestBody = null;//用于將流保存下來
    public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        requestBody = StreamUtils.copyToByteArray(request.getInputStream());
    }
    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {
            @Override
            public int read(){
                return bais.read();  // 讀取 requestBody 中的數(shù)據(jù)
            }
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) { }
        };
    }
    @Override
    public BufferedReader getReader() throws IOException{
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}

2.編寫Filter

package com.xqw.kyg.filter;
import com.xqw.kyg.util.MyHttpServletRequestWrapper;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
public class HttpServletRequestReplacedFilter implements Filter {
    @Override
    public void destroy() {}
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if(request instanceof HttpServletRequest) {
            requestWrapper = new MyHttpServletRequestWrapper((HttpServletRequest) request);
        }
        if(requestWrapper == null) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }
    @Override
    public void init(FilterConfig arg0) throws ServletException {}
}

到此代碼編寫就完成了。你現(xiàn)在可以在 過濾器 中讀取inputstream數(shù)據(jù)controller中也可以獲取到了。其實(shí)代碼并不是特別困難。主要是出現(xiàn)BUG能及時(shí)的想到原因,和提供解決方案。

到此,相信大家對(duì)“如何解決SpringBoot攔截器讀取流后不能再讀取的問題”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI