拦截器中,request中getReader()和getInputStream()只能调用一次,构建可重复读取inputStream的request.
由于 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; } }