使用ServletInputStream在攔截器或過(guò)濾器中應(yīng)用后重寫
ServletInputStream在攔截器或過(guò)濾器應(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中無(wú)法獲取請(qǐng)求參數(shù)
問(wèn)題描述
在攔截器種使用了request.getInputStream()或者getReader(),然后在controller接口種使用了@requestbody ,導(dǎo)致controller中無(wú)法獲取入?yún)?,?bào)錯(cuò):HttpMessageNotReadableException: Required request body is missing:
原因分析
ServletRequest中g(shù)etReader()和getInputStream()只能調(diào)用一次。而又由于@RequestBody注解獲取輸出參數(shù)的方式也是根據(jù)流的方式獲取的。所以我們前面使用流獲取后,后面的@RequestBody就獲取不到對(duì)應(yīng)的輸入流了。
為什么取不到輸入流了???因?yàn)榱鲗?duì)應(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保存下來(lái),然后通過(guò)過(guò)濾器把保存下來(lái)的request再填充進(jìn)去,這樣就可以多次讀取request了。
第一步:定義過(guò)濾器
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)入了過(guò)濾器。。。。。"); 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)類中注冊(cè)過(guò)濾器
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java設(shè)計(jì)模式之簡(jiǎn)單工廠 工廠方法 抽象工廠深度總結(jié)
設(shè)計(jì)模式(Design Pattern)是前輩們對(duì)代碼開(kāi)發(fā)經(jīng)驗(yàn)的總結(jié),是解決特定問(wèn)題的一系列套路。它不是語(yǔ)法規(guī)定,而是一套用來(lái)提高代碼可復(fù)用性、可維護(hù)性、可讀性、穩(wěn)健性以及安全性的解決方案2021-09-09Java和scala實(shí)現(xiàn) Spark RDD轉(zhuǎn)換成DataFrame的兩種方法小結(jié)
今天小編就為大家分享一篇Java和scala實(shí)現(xiàn) Spark RDD轉(zhuǎn)換成DataFrame的兩種方法小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06java非遞歸實(shí)現(xiàn)之二叉樹(shù)的前中后序遍歷詳解
樹(shù)的遍歷順序大體分為三種:前序遍歷(先根遍歷、先序遍歷),中序遍歷(中根遍歷),后序遍歷(后根遍歷),本文將給大家詳細(xì)的介紹,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值2021-09-09Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(49)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-08-08Spring Boot 自動(dòng)配置的實(shí)現(xiàn)
這篇文章主要介紹了Spring Boot 自動(dòng)配置的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08IntelliJ IDEA 2020最新激活碼(親測(cè)有效,可激活至 2089 年
這篇文章主要介紹了IntelliJ IDEA 2021最新激活碼(親測(cè)有效,可激活至 2089 年),非常不錯(cuò),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04詳解Spring Boot加載properties和yml配置文件
本篇文章主要介紹了詳解Spring Boot加載properties和yml配置文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04java web中使用cookie記住用戶的賬號(hào)和密碼
這篇文章主要介紹了java web中使用cookie記住用戶的賬號(hào)和密碼的相關(guān)資料,需要的朋友可以參考下2017-01-01