由於 request中getReader()和getInputStream()只能調用一次
在項目中,可能會出現需要針對接口參數進行校驗等問題。
因此,針對這問題,給出一下解決方案
實現方法:先將RequestBody保存為一個byte數組,然后通過Servlet自帶的HttpServletRequestWrapper類覆蓋getReader()和getInputStream()方法,使流從保存的byte數組讀取。然后再Filter中將ServletRequest替換為ServletRequestWrapper。代碼如下:
BodyReaderHttpServletRequestWrapper類包裝ServletRequest,將流保存為byte[],然后將getReader()和getInputStream()方法的流的讀取指向byte[]
http://zhangbo-peipei-163-com.iteye.com/blog/2022460
step 1:
添加RepeatedlyReadRequestWrapper 類並繼承 HttpServletRequestWrapper 包裝類
package com.config; import org.apache.commons.lang3.StringUtils; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; public class RepeatedlyReadRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public RepeatedlyReadRequestWrapper(HttpServletRequest request) throws IOException { super(request); body = readBytes(request.getReader(), "utf-8"); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener listener) { } @Override public int read() throws IOException { return bais.read(); } }; } /** * 通過BufferedReader和字符編碼集轉換成byte數組 * @param br * @param encoding * @return * @throws IOException */ private byte[] readBytes(BufferedReader br,String encoding) throws IOException{ String str = null,retStr=""; while ((str = br.readLine()) != null) { retStr += str; } if (StringUtils.isNotBlank(retStr)) { return retStr.getBytes(Charset.forName(encoding)); } return null; } }
step 2:
添加 RepeatedlyReadFilter 實現 filter 過濾器接口方法,當客戶端的請求先 過濾 進入SpringMvc Dispatch 路由前先包裝下
package com.filter; import com.config.RepeatedlyReadRequestWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * 復制請求數據包body * 以提供 攔截器下 可數次獲取Body數據包*/ public class RepeatedlyReadFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(RepeatedlyReadFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { logger.debug("復制request.getInputStream流"); ServletRequest requestWrapper = null; if (request instanceof HttpServletRequest) { requestWrapper = new RepeatedlyReadRequestWrapper((HttpServletRequest) request); } if (null == requestWrapper) { chain.doFilter(request, response); } else { chain.doFilter(requestWrapper, response); } } @Override public void destroy() { } }
step 3:
添加攔截器 RepeatedlyReadInterceptor 繼承 HandlerInterceptorAdapter 攔截適配器
package com.interceptor; import com.config.RepeatedlyReadRequestWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.nio.charset.Charset; public class RepeatedlyReadInterceptor extends HandlerInterceptorAdapter { private static final Logger logger = LoggerFactory.getLogger(RepeatedlyReadInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { /** * 對來自后台的請求統一進行日志處理 */ RepeatedlyReadRequestWrapper requestWrapper; if (request instanceof RepeatedlyReadRequestWrapper) { // 簽名處理過程 start.... requestWrapper = (RepeatedlyReadRequestWrapper) request; logger.info("請求Body: {} ", getBodyString(requestWrapper)); // 簽名處理過程 end.... } // 默認記錄后台接口請求日志記錄 String url = request.getRequestURL().toString(); String method = request.getMethod(); String uri = request.getRequestURI(); String queryString = request.getQueryString(); logger.info(String.format("請求參數, url: %s, method: %s, uri: %s, params: %s ", url, method, uri, queryString)); return super.preHandle(request, response, handler); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { RepeatedlyReadRequestWrapper requestWrapper; if (request instanceof RepeatedlyReadRequestWrapper) { // 測試再次獲取Body start.... requestWrapper = (RepeatedlyReadRequestWrapper) request; logger.info("請求Body: {} ", getBodyString(requestWrapper)); // 測試再次獲取Body end.... } } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } /** * 獲取請求Body * * @param request * * @return */ public static String getBodyString(final ServletRequest request) { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = cloneInputStream(request.getInputStream()); reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } 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(); } /** * Description: 復制輸入流</br> * * @param inputStream * * @return</br> */ public static InputStream cloneInputStream(ServletInputStream inputStream) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; try { while ((len = inputStream.read(buffer)) > -1) { byteArrayOutputStream.write(buffer, 0, len); } byteArrayOutputStream.flush(); } catch (IOException e) { e.printStackTrace(); } InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); return byteArrayInputStream; } }
step 4:
配置過濾器與攔截器 WebMvcConfig
package com.config; import com.filter.RepeatedlyReadFilter; import com.interceptor.MyInterceptor; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.*; /** * SpringMVC 配置類*/ @Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new RepeatedlyReadInterceptor()).addPathPatterns("/**"); super.addInterceptors(registry); } @Bean public FilterRegistrationBean repeatedlyReadFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); RepeatedlyReadFilter repeatedlyReadFilter = new RepeatedlyReadFilter(); registration.setFilter(repeatedlyReadFilter); registration.addUrlPatterns("/*"); return registration; } }