在SpringBoot的攔截器中通過流 ( request.getInputStream() ) 的方式讀取body中傳來的數據會導致controller接收不到值。
這個問題其實就是一個流讀取的問題,眾所周知在Java中input流只能讀取一次,主要原因是通標記的方法來判斷流是否讀取完畢(讀取位 -1就是流讀取完畢)
解決這個問題我能想到兩種方式
1.通過修改標記的方式 ( inputstream.markSupported() 方法可以判斷這個流是否支持 mark 和 reset 方法。他們分別是標記 和 重新定位流。)
2.將流賦值給一個 byte[] 數組,或者其他變量保存起來。下載讀取流時就調用這個數組就行。
第一種方法
再回到問題上來我們可以先使用第一種方法判斷 requet 中的inputStream 是否支持標記和重新定位。因為這種方式實現起來比較簡單。無需考慮太多。
1
2
3
4
5
6
|
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { boolean b = request.getInputStream().markSupported(); System.out.println(b); } // 輸出結果為 false |
上述代碼會返回一個 false 那么很明顯,request 中的 input 流是不支持標記和重新定位的。
第二種方法
我們再考慮第二種方法,我們需要一個變量保存這個流。并且還要保證再過濾器中和controller中都要拿到這個變量。直接定義一個全局變量獲取修改傳值方式,都是可以的。全局變量這種方式我就不演示了。
下面是改變傳值方式的 demo
1
2
3
4
5
6
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { ServletInputStream inputStream1 = request.getInputStream(); // 各種對 inputStream1 處理的操作... Object obejct = inputStream1; request.setAttribute( "Params" ,obejct); } |
這樣就可以再controller那邊就可以直接獲取 Attribute 中的值。
但是!這樣有很大的局限性,例如: 我已經寫好了大多數的controller方法體。這時再改用這種方式傳值。對于開發人員是一種莫大的痛苦 -_- 于是通過不屑的百度查詢到另一種方法 一一一 改寫HttpServletRequestWrapper方法。為什么是這個方法?因為他實現了 HttpServletRequest 這個接口。也是我們攔截器接收的 request 要求的類型。首先我們先看一張圖。** 注意 filter 和 inteceptor 中間。 **
通過上方這個圖我們可以知道,在 Filter 和 Inteceptor 中間有一層Servlet。而Servlet就是提交request的地方。所以我們要重寫HttpServletRequest方法只能在Servlet之前。也就是filter 中。下面就是直接上代碼了
1.重寫 HttpServletRequest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
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 中的數據 } @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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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 {} } |
到此代碼編寫就完成了。你現在可以在 過濾器 中讀取inputstream數據controller中也可以獲取到了。其實代碼并不是特別困難。主要是出現BUG能及時的想到原因,和提供解決方案。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/weixin_39933264/article/details/100181291