拦截器中,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;
}
}
拦截器中,request中getReader()和getInputStream()只能调用一次,构建可重复读取inputStream的request.的更多相关文章
- 关于springmvc时request的getReader()和getInputStream()只能调用一次的解决办法
		最近准备在原有的SSM项目的基础上添加完善的日志分析,由于是APP的后台系统,之前在规划APP的时候,并没有在APP上做埋点的处理,而如果想要进行埋点处理的话,对于未能新升级的APP用户来说,就是去了 ... 
- ServletRequest中getReader()和getInputStream()只能调用一次的解决办法(转)
		原文地址:http://liwx2000.iteye.com/blog/1542431 原文作者:liwx2000 为了提高项目安全性,拦截非法访问,要给项目增加了一个过滤器,拦截所有的请求,校验是否 ... 
- ServletRequest中getReader()和getInputStream()只能调用一次的解决办法
		转载:http://blog.sina.com.cn/s/blog_870cd7b90101fg58.html 最近使用spring mvc做项目,数据格式是json,有一个功能是实现记录请求的参数, ... 
- mybatis - 基于拦截器修改执行中的SQL语句
		拦截器介绍 mybatis提供了@Intercepts注解允许开发者对mybatis的执行器Executor进行拦截. Executor接口方法主要有update.query.commit.rollb ... 
- struts2中方法拦截器(Interceptor)的中的excludeMethods与includeMethods的理解
		http://www.cnblogs.com/langtianya/archive/2013/04/10/3012205.html 
- 解决SpringMVC拦截器中Request数据只能读取一次的问题
		解决SpringMVC拦截器中Request数据只能读取一次的问题 开发项目中,经常会直接在request中取数据,如Json数据,也经常用到@RequestBody注解,也可以直接通过request ... 
- 9.springMVC中的拦截器
		springMVC中的拦截器大概大致可以分为以下几个步骤去学习: 1.自定义一个类实现HandlerInterceptor接口,这里要了解其中几个方法的作用 2.在springMVC的配置文件中添加拦 ... 
- (转)spring中的拦截器(HandlerInterceptor+MethodInterceptor)
		1. 过滤器跟拦截器的区别 在说拦截器之前,不得不说一下过滤器,有时候往往被这两个词搞的头大. 其实我们最先接触的就是过滤器,还记得web.xml中配置的<filter>吗~ 你应该知道 ... 
- SpringMVC中的拦截器、过滤器的区别、处理异常
		1. SpringMVC中的拦截器(Interceptor) 1.1. 作用 拦截器是运行在DispatcherServlet之后,在每个Controller之前的,且运行结果可以选择放行或拦截! 除 ... 
随机推荐
- 使用rsync在linux(服务端)与windows(客户端)之间同步
			说明: 1.RsyncServer服务端 系统:CentOS 6.8 IP地址:192.168.247.141 2.Rsync客户端 系统:Windows10 实现目的: Rsync客户端同步服务端/ ... 
- [gym101981M][2018ICPC南京M题]Mediocre String Problem
			题目链接 题目大意是问在$S$串中找区间$[i,j]$,在$T$串中找位置$k$,使得$S[i,j]$和$T[1,k]$可以组成回文串,并且$j-i+1>k$,求这样的三元组$(i,j,k)$的 ... 
- Java基础——Modifier类
			转自:https://www.cnblogs.com/baiqiantao/p/7478523.html 反射 Reflect Modifier 修饰符工具类 在查看反射相关的Class.Fiel ... 
- vue-cli3.0本地代理cookie跨域请求Nginx配置
			由于后端需要通过请求取前端中的cookie信息,在本地开发模式中,直接请求接口,后端无法拿到前端cookie数据, 经测试需在 vue-cli 中使用代理,如果使用Nginx做反向代理需同时修改Ngi ... 
- 剑指offer-3:跳阶梯
			三.跳台阶 题目描述 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果). 分析 青蛙每一次跳跃只有两种选择:一是再跳1级阶梯到达第n ... 
- 上载和下载CSV文件
			sap中把txt .excel .文件上载到内表中,txt和csv速度最快. excel文件导出的csv是用,分隔符分隔的,如果单元格的文本中就有逗号,这样会和分隔符逗号混淆,最好abap产生csv文 ... 
- Java中遍历Set集合的方法
			对 set 的遍历 1.迭代遍历: Set<String> set = new HashSet<String>(); Iterator<String> it = s ... 
- linux命令详解——tar
			tar [-cxtzjvfpPN] 文件与目录 .... [参数]: -c :建立一个压缩文件的参数指令(create 的意思): -x :解开一个压缩文件的参数指令! -t :查看 tarfile ... 
- awk 快速批量kill 进程
			ps -ef|grep aaa|grep -v grep|awk '{print "kill -9 " $2}' |sh 
- linux c 链接详解3-静态库
			3静态库 摘自:Linux C编程一站式学习 透过本节可以学会编译静态链接库的shell脚本! 有时候需要把一组代码编译成一个库,这个库在很多项目中都要用到,例如libc就是这样一个库,我们在不同的程 ... 
