使用ServletInputStream在攔截器或過濾器中應用後重寫
ServletInputStream在攔截器或過濾器應用後重寫
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中數據的代碼 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()
導致在controller中無法獲取請求參數
問題描述
在攔截器種使用瞭request.getInputStream()或者getReader(),然後在controller接口種使用瞭@requestbody ,導致controller中無法獲取入參,報錯:HttpMessageNotReadableException: Required request body is missing:
原因分析
ServletRequest中getReader()和getInputStream()隻能調用一次。而又由於@RequestBody註解獲取輸出參數的方式也是根據流的方式獲取的。所以我們前面使用流獲取後,後面的@RequestBody就獲取不到對應的輸入流瞭。
為什麼取不到輸入流瞭???因為流對應的是數據,數據放在內存中,有的是部分放在內存中。
read 一次標記一次當前位置(mark position),第二次read就從標記位置繼續讀(從內存中copy)數據。
所以這就是為什麼讀瞭一次第二次是空瞭。 怎麼讓它不為空呢?隻要inputstream 中的pos 變成0就可以重寫讀取當前內存中的數據。
javaAPI中有一個方法public void reset() 這個方法就是可以重置pos為起始位置,但是不是所有的IO讀取流都可以調用該方法!ServletInputStream是不能調用reset方法,這就導致瞭隻能調用一次getInputStream()。
如何處理
重寫HttpServletRequestWrapper把request保存下來,然後通過過濾器把保存下來的request再填充進去,這樣就可以多次讀取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("進入瞭過濾器。。。。。"); 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; } }
第三步:在啟動類中註冊過濾器
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- 在攔截器中讀取request參數,解決在controller中無法二次讀取的問題
- springboot接口如何多次獲取request中的body內容
- SpringBoot配置自定義攔截器實現過程詳解
- 使用@RequestBody傳遞多個不同對象方式
- Springboot如何利用攔截器攔截請求信息收集到日志詳解