拦截器中,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之前的,且运行结果可以选择放行或拦截! 除 ...
随机推荐
- mongodb增删改查常用命令总结
前言 去年我还折腾过mongodb,后来用不到也就没碰了,这就导致了我忘的一干二净,不得不感叹,编程这东西只要不用,就会忘没了.现在我想重拾mongodb,来总结一下常用命令,主要就是增删改查. 另外 ...
- Java 将Maven项目打成可执行jar包
一.用maven-shade-plugin打包 在pom.xml文件中加入如下信息,利用Maven的maven-shade-plugin插件进行打包. <build> <plugin ...
- Tarjan水题系列(5):最大半连通子图 [ZJOI2007 luogu P2272]
题目 大意: 缩点后转为求最长链的长度和最长链的个数 思路: 看懂题就会做系列 长度和个数都可以拓扑排序后DP求得 毕竟是2007年的题 代码: 如下 #include <cstdio> ...
- 请求转发forward()和URL重定向redirect()的区别
- vue项目--vuex状态管理器
本文取之官网和其他文章结合自己的理解用简单化的语言表达.用于自己的笔记记录,也希望能帮到其他小伙伴理解,学习更多的前端知识. Vuex 是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态 ...
- Flask开发系列之Flask+redis实现IP代理池
Flask开发系列之Flask+redis实现IP代理池 代理池的要求 多站抓取,异步检测:多站抓取:指的是我们需要从各大免费的ip代理网站,把他们公开的一些免费代理抓取下来:一步检测指的是:把这些代 ...
- Core Graphics 定制UIVIew 处理图片
许多UIView的子类,如UIButton或UILabel,它们的形状都是系统固定的.但是,对于一些特殊的情况,我们需要绘制产品狗想要的图形.那么等待我们的只有两个选择:第一,可以使用UIImageV ...
- Java Script入门
学习来源:https://www.runoob.com/js/js-tutorial.html JavaScript 教程 JavaScript 是 Web 的编程语言. 所有现代的 HTML 页面都 ...
- 转载:利用php数组函数进行函数式编程
因为一个BUG, 我在一个摇摇欲坠,几乎碰一下就会散架的项目中某一个角落中发现下面这样一段代码 这段程序与那个BUG有密切的关系. 我来回反复的捉摸这段代码, 发现这段代码实现了两个功能 第一个是在一 ...
- Service_Worker XSS
0x00 简介 Service Worker 是 Chrome 团队提出和力推的一个 WEB API,用于给 web 应用提供高级的可持续的后台处理能力.该 WEB API 标准起草于 2013 年, ...