SpringBoot攔截器讀取流后不能再讀取的問(wèn)題
在SpringBoot的攔截器中通過(guò)流 ( request.getInputStream() ) 的方式讀取body中傳來(lái)的數(shù)據(jù)會(huì)導(dǎo)致controller接收不到值。
這個(gè)問(wèn)題其實(shí)就是一個(gè)流讀取的問(wèn)題,眾所周知在Java中input流只能讀取一次,主要原因是通標(biāo)記的方法來(lái)判斷流是否讀取完畢(讀取位 -1就是流讀取完畢)
解決這個(gè)問(wèn)題我能想到兩種方式
1.通過(guò)修改標(biāo)記的方式 ( inputstream.markSupported() 方法可以判斷這個(gè)流是否支持 mark 和 reset 方法。他們分別是標(biāo)記 和 重新定位流。)
2.將流賦值給一個(gè) byte[] 數(shù)組,或者其他變量保存起來(lái)。下載讀取流時(shí)就調(diào)用這個(gè)數(shù)組就行。
第一種方法
再回到問(wèn)題上來(lái)我們可以先使用第一種方法判斷 requet 中的inputStream 是否支持標(biāo)記和重新定位。因?yàn)檫@種方式實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單。無(wú)需考慮太多。
@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è)流。并且還要保證再過(guò)濾器中和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ā)人員是一種莫大的痛苦 -_- 于是通過(guò)不屑的百度查詢到另一種方法 一一一 改寫HttpServletRequestWrapper方法。為什么是這個(gè)方法?因?yàn)樗麑?shí)現(xiàn)了 HttpServletRequest 這個(gè)接口。也是我們攔截器接收的 request 要求的類型。首先我們先看一張圖。** 注意 filter 和 inteceptor 中間。 **

通過(guò)上方這個(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;//用于將流保存下來(lái)
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)在可以在 過(guò)濾器 中讀取inputstream數(shù)據(jù)controller中也可以獲取到了。其實(shí)代碼并不是特別困難。主要是出現(xiàn)BUG能及時(shí)的想到原因,和提供解決方案。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot前端傳參date類型后臺(tái)處理的方式
這篇文章主要介紹了springboot前端傳參date類型后臺(tái)處理的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
SpringBoot中Elasticsearch的連接配置原理與使用詳解
Elasticsearch是一種開源的分布式搜索和數(shù)據(jù)分析引擎,它可用于全文搜索、結(jié)構(gòu)化搜索、分析等應(yīng)用場(chǎng)景,本文主要介紹了SpringBoot中Elasticsearch的連接配置原理與使用詳解,感興趣的可以了解一下2023-09-09
在Java和PostgreSQL枚舉之間轉(zhuǎn)換的通用方法
枚舉類型(enum)是一種方便的數(shù)據(jù)類型,允許我們指定一個(gè)常量列表,對(duì)象字段或數(shù)據(jù)庫(kù)列可以設(shè)置為該列表中的值,在本文中,我將回顧處理Java和PostgreSQL枚舉轉(zhuǎn)換的通用方法,需要的朋友可以參考下2023-10-10
實(shí)例分析Java中public static void main(String args[])是什么意思
這篇文章主要介紹了實(shí)例分析Java中public static void main(String args[])的意義,詳細(xì)分析了Java主函數(shù)main關(guān)鍵字聲明的具體含義和用法,需要的朋友可以參考下2015-12-12
Spring Boot加密配置文件特殊內(nèi)容的示例代碼詳解
這篇文章主要介紹了Spring Boot加密配置文件特殊內(nèi)容的相關(guān)知識(shí),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
解決idea2024版本創(chuàng)建項(xiàng)目時(shí)沒(méi)有java?8的版本選擇
這篇文章主要介紹了在使用IntelliJ?IDEA創(chuàng)建Spring?Boot項(xiàng)目時(shí)遇到的問(wèn)題,包括Java版本選擇受限和項(xiàng)目結(jié)構(gòu)不完整,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2025-03-03
Java實(shí)現(xiàn)AOP功能的封裝與配置的小框架實(shí)例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)AOP功能的封裝與配置的小框架實(shí)例代碼,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02
javax.validation.constraints如何校驗(yàn)參數(shù)合法性
本文將深入探討javax.validation.constraints的基本用法和高級(jí)應(yīng)用,幫助讀者更好地理解和運(yùn)用這個(gè)強(qiáng)大的校驗(yàn)框架,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
Java如何實(shí)現(xiàn)通過(guò)鍵盤輸入一個(gè)數(shù)組
這篇文章主要介紹了Java實(shí)現(xiàn)通過(guò)鍵盤輸入一個(gè)數(shù)組,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02

