經過在攔截器中獲取request中的json數據,咱們能夠實現對參數進行校驗和改寫。問題是參數只能在攔截器裏獲取一次,日後在controller層就沒法獲取數據,提示body爲空。java
在網上查找資料後發現,request的輸入流只能讀取一次,那麼這是爲何呢?json
那是由於流對應的是數據,數據放在內存中,有的是部分放在內存中。 read 一次標記一次當前位置(mark position),第二次read就從標記位置繼續讀(從內存中copy)數據。 因此這就是爲何讀了一次第二次是空了。 怎麼讓它不爲空呢? 只要inputstream 中的pos 變成0就能夠重寫讀取當前內存中的數據。javaAPI中有一個方法public void reset() 這個方法就是能夠重置pos爲起始位置,可是不是全部的IO讀取流均可以調用該方法! ServletInputStream是不能調用reset方法,這就致使了只能調用一次getInputStream()
HttpServletRequestWrapper是 httpServletRequest 的包裝類app
新建一個類繼承HttpServletRequestWrapper實現對 httpServletRequest 的裝飾,用來獲取 body 數據ide
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; private String bodyStr; public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); String bodyString = getBodyString(request); body = bodyString.getBytes(Charset.forName("UTF-8")); bodyStr=bodyString; } public String getBodyStr() { return bodyStr; } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return byteArrayInputStream.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } public String getBodyString(HttpServletRequest request) throws IOException { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = request.getInputStream(); reader = new BufferedReader( new InputStreamReader(inputStream, Charset.forName("UTF-8"))); char[] bodyCharBuffer = new char[1024]; int len = 0; while ((len = reader.read(bodyCharBuffer)) != -1) { sb.append(new String(bodyCharBuffer, 0, len)); } } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } }
再新建一個 filter 實現對傳入的 httpServletRequest 的轉換post
@WebFilter(filterName = "httpServletRequestWrapperFilter", urlPatterns = {"/*"}) public class HttpServletRequestWrapperFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletRequest requestWrapper = null; if (request instanceof HttpServletRequest) { HttpServletRequest httpRequest = (HttpServletRequest) request; //遇到post方法纔對request進行包裝 String methodType = httpRequest.getMethod(); if ("POST".equals(methodType)) { requestWrapper = new BodyReaderHttpServletRequestWrapper( (HttpServletRequest) request); } } if (null == requestWrapper) { chain.doFilter(request, response); } else { chain.doFilter(requestWrapper, response); } } @Override public void destroy() { } }
最後在攔截器就能夠獲取request中body數據測試
if(request instanceof BodyReaderHttpServletRequestWrapper ){ System.out.println(((BodyReaderHttpServletRequestWrapper) request).getBodyStr()); }
經測試發現並不影響controller層獲取body數據ui
爲何須要在 filter 裏進行對 httpServletRequest 的包裝轉換,直接在攔截器裏進行包裝不行嘛?url
過濾器(Filter)和攔截器(Interceptor)之間的最大區別就是,過濾器能夠包裝Request和Response,而攔截器並不能 用代碼描述攔截器和過濾器的流程大概就是這樣的: 攔截器:void run () { Request request = new Request(); preHandle(request); service(request); } preHandler(Request request) { request = new RequestWrapper(request); //在這裏修改Request的引用,不會影響到service方法的request } 過濾器void run () { Request request = new Request(); doFilter(request); } doFilter(Request request) { request = new RequestWrapper(request); //在這裏修改Request的引用,會影響到service方法的request service(request); }