使用ServletInputStream在攔截器或過濾器中應(yīng)用后重寫
ServletInputStream在攔截器或過濾器應(yīng)用后重寫
ServletInputStream inputStream = super.getInputStream();
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8")));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (servletInputStream != null) {
try {
servletInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//使用ServletInputStream中數(shù)據(jù)的代碼
byte[] bytes = sb.getBytes("UTF-8");
final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bais.read();
}
};
在攔截器種使用了request.getInputStream()或者getReader()
導(dǎo)致在controller中無法獲取請求參數(shù)
問題描述
在攔截器種使用了request.getInputStream()或者getReader(),然后在controller接口種使用了@requestbody ,導(dǎo)致controller中無法獲取入?yún)?,?bào)錯(cuò):HttpMessageNotReadableException: Required request body is missing:
原因分析
ServletRequest中g(shù)etReader()和getInputStream()只能調(diào)用一次。而又由于@RequestBody注解獲取輸出參數(shù)的方式也是根據(jù)流的方式獲取的。所以我們前面使用流獲取后,后面的@RequestBody就獲取不到對應(yīng)的輸入流了。
為什么取不到輸入流了???因?yàn)榱鲗?yīng)的是數(shù)據(jù),數(shù)據(jù)放在內(nèi)存中,有的是部分放在內(nèi)存中。
read 一次標(biāo)記一次當(dāng)前位置(mark position),第二次read就從標(biāo)記位置繼續(xù)讀(從內(nèi)存中copy)數(shù)據(jù)。
所以這就是為什么讀了一次第二次是空了。 怎么讓它不為空呢?只要inputstream 中的pos 變成0就可以重寫讀取當(dāng)前內(nèi)存中的數(shù)據(jù)。
javaAPI中有一個(gè)方法public void reset() 這個(gè)方法就是可以重置pos為起始位置,但是不是所有的IO讀取流都可以調(diào)用該方法!ServletInputStream是不能調(diào)用reset方法,這就導(dǎo)致了只能調(diào)用一次getInputStream()。
如何處理
重寫HttpServletRequestWrapper把request保存下來,然后通過過濾器把保存下來的request再填充進(jìn)去,這樣就可以多次讀取request了。
第一步:定義過濾器
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/*", filterName = "channelFilter")
public class ChannelFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
ServletRequest requestWrapper = null;
if (servletRequest instanceof HttpServletRequest) {
requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
}
if (requestWrapper == null) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
System.out.println("進(jìn)入了過濾器。。。。。");
filterChain.doFilter(requestWrapper, servletResponse);
}
}
@Override
public void destroy() {
}
}
第二步:重寫RequestWrapper類
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
}
第三步:在啟動(dòng)類中注冊過濾器

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
spring?boot使用@Async注解解決異步多線程入庫的問題
最近在寫項(xiàng)目是需要添加異步操作來提高效率,所以下面這篇文章主要給大家介紹了關(guān)于spring?boot使用@Async注解解決異步多線程入庫問題的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05
SpringBoot自定義starter啟動(dòng)器的實(shí)現(xiàn)思路
這篇文章主要介紹了SpringBoot如何自定義starter啟動(dòng)器,通過starter的自定義過程,能夠加深大家對SpringBoot自動(dòng)配置原理的理解,需要的朋友可以參考下2022-10-10
詳解springboot中各個(gè)版本的redis配置問題
這篇文章主要介紹了詳解springboot中各個(gè)版本的redis配置問題,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
SpringBoot使用TraceId進(jìn)行日志鏈路追蹤的實(shí)現(xiàn)步驟
有時(shí)候一個(gè)業(yè)務(wù)調(diào)用鏈場景,很長,調(diào)了各種各樣的方法,看日志的時(shí)候,各個(gè)接口的日志穿插,確實(shí)讓人頭大,所以為了解決這個(gè)問題,本文給大家介紹了SpringBoot使用TraceId進(jìn)行日志鏈路追蹤的實(shí)現(xiàn)步驟,需要的朋友可以參考下2024-11-11
Android中比較常見的Java super關(guān)鍵字
這篇文章主要為大家介紹了Android中比較常見的Java super關(guān)鍵字,具有一定的學(xué)習(xí)參考價(jià)值,感興趣的小伙伴們可以參考一下2016-01-01
Java實(shí)現(xiàn)優(yōu)雅的參數(shù)校驗(yàn)方法詳解
這篇文章主要為大家詳細(xì)介紹了Java語言如何實(shí)現(xiàn)優(yōu)雅的參數(shù)校驗(yàn),文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Java有一定是幫助,需要的可以參考一下2022-06-06

