由于 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.的更多相关文章

  1. 关于springmvc时request的getReader()和getInputStream()只能调用一次的解决办法

    最近准备在原有的SSM项目的基础上添加完善的日志分析,由于是APP的后台系统,之前在规划APP的时候,并没有在APP上做埋点的处理,而如果想要进行埋点处理的话,对于未能新升级的APP用户来说,就是去了 ...

  2. ServletRequest中getReader()和getInputStream()只能调用一次的解决办法(转)

    原文地址:http://liwx2000.iteye.com/blog/1542431 原文作者:liwx2000 为了提高项目安全性,拦截非法访问,要给项目增加了一个过滤器,拦截所有的请求,校验是否 ...

  3. ServletRequest中getReader()和getInputStream()只能调用一次的解决办法

    转载:http://blog.sina.com.cn/s/blog_870cd7b90101fg58.html 最近使用spring mvc做项目,数据格式是json,有一个功能是实现记录请求的参数, ...

  4. mybatis - 基于拦截器修改执行中的SQL语句

    拦截器介绍 mybatis提供了@Intercepts注解允许开发者对mybatis的执行器Executor进行拦截. Executor接口方法主要有update.query.commit.rollb ...

  5. struts2中方法拦截器(Interceptor)的中的excludeMethods与includeMethods的理解

    http://www.cnblogs.com/langtianya/archive/2013/04/10/3012205.html

  6. 解决SpringMVC拦截器中Request数据只能读取一次的问题

    解决SpringMVC拦截器中Request数据只能读取一次的问题 开发项目中,经常会直接在request中取数据,如Json数据,也经常用到@RequestBody注解,也可以直接通过request ...

  7. 9.springMVC中的拦截器

    springMVC中的拦截器大概大致可以分为以下几个步骤去学习: 1.自定义一个类实现HandlerInterceptor接口,这里要了解其中几个方法的作用 2.在springMVC的配置文件中添加拦 ...

  8. (转)spring中的拦截器(HandlerInterceptor+MethodInterceptor)

    1.  过滤器跟拦截器的区别 在说拦截器之前,不得不说一下过滤器,有时候往往被这两个词搞的头大. 其实我们最先接触的就是过滤器,还记得web.xml中配置的<filter>吗~ 你应该知道 ...

  9. SpringMVC中的拦截器、过滤器的区别、处理异常

    1. SpringMVC中的拦截器(Interceptor) 1.1. 作用 拦截器是运行在DispatcherServlet之后,在每个Controller之前的,且运行结果可以选择放行或拦截! 除 ...

随机推荐

  1. HDUST-1245 Interpreter(模拟)

    1245: Problem E: Interpreter 时间限制: 1 Sec  内存限制: 128 MB提交: 4  解决: 2[提交][状态][讨论版] 题目描述 Problem E: Inte ...

  2. Python程序结构(sys、os)

    一.Python组成结构 Python程序一般由包package.模块moudle.函数function组成.具体关系如下图:

  3. go相关资料

    1.go的调度2.go struct能不能比较 因为是强类型语言,所以不同类型的结构不能作比较,但是同一类型的实例值是可以比较的,实例不可以比较,因为是指针类型 3.go defer(for defe ...

  4. [.net core]9.中间件的具体实现

    查看Startup.cs的configure方法 public void Configure(IApplicationBuilder app, IHostingEnvironment env) { i ...

  5. java延时队列

    应用场景 1)7天自动收货 a.用户支付完成以后,把订单ID插入到内存的一个DelayQueue中,同时插入到Redis中. b.7天之内,用户点击了确认收货,则从DelayQueue中删除,从Red ...

  6. python 四舍五入进位不准

    python中四舍五入进位不准,自己写了个方法结果: def new_round(_float, _len): ''' 四舍五入保留小数位,如果想实现 0.2 显示为 0.20,可使用 '%.2f' ...

  7. Maven项目构建利器05——Maven的生命周期

    Maven各个构建环节执行的顺序: 不能打乱顺序, 必须按照既定的正确顺序(编译,测试.打包.部署)来执行Maven的核心程序中定义了抽象的生命周期, 生命周期中各个阶段的具体任务是由插件来完成的,可 ...

  8. tf.get_variable函数的使用

    tf.get_variable(name,  shape, initializer): name就是变量的名称,shape是变量的维度,initializer是变量初始化的方式,初始化的方式有以下几种 ...

  9. Big Data(五)关于Hadoop的HA的实践搭建

    JoinNode 分布在node01,node02,node03 1.停止之前的集群 2.免密:node01,node02 node02: cd ~/.ssh ssh-keygen -t dsa -P ...

  10. 清北学堂提高组突破营游记day4

    今天主攻图论. 对于这道题,30分做法是暴力搜索全部来判断是否有异样. 对于满分做法,利用带权并查集.? 又带我们串了一边LCA 安利个人LCA博客. spfa代码.原理:循环队列. 然后是floyd ...