由于 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 包装类

  1. package com.config;
  2.  
  3. import org.apache.commons.lang3.StringUtils;
  4.  
  5. import javax.servlet.ReadListener;
  6. import javax.servlet.ServletInputStream;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletRequestWrapper;
  9. import java.io.BufferedReader;
  10. import java.io.ByteArrayInputStream;
  11. import java.io.IOException;
  12. import java.io.InputStreamReader;
  13. import java.nio.charset.Charset;
  14.  
  15. public class RepeatedlyReadRequestWrapper extends HttpServletRequestWrapper {
  16. private final byte[] body;
  17.  
  18. public RepeatedlyReadRequestWrapper(HttpServletRequest request)
  19. throws IOException {
  20. super(request);
  21. body = readBytes(request.getReader(), "utf-8");
  22. }
  23.  
  24. @Override
  25. public BufferedReader getReader() throws IOException {
  26. return new BufferedReader(new InputStreamReader(getInputStream()));
  27. }
  28.  
  29. @Override
  30. public ServletInputStream getInputStream() throws IOException {
  31. final ByteArrayInputStream bais = new ByteArrayInputStream(body);
  32. return new ServletInputStream() {
  33.  
  34. @Override
  35. public boolean isFinished() {
  36. return false;
  37. }
  38.  
  39. @Override
  40. public boolean isReady() {
  41. return false;
  42. }
  43.  
  44. @Override
  45. public void setReadListener(ReadListener listener) {
  46.  
  47. }
  48.  
  49. @Override
  50. public int read() throws IOException {
  51. return bais.read();
  52. }
  53. };
  54. }
  55.  
  56. /**
  57. * 通过BufferedReader和字符编码集转换成byte数组
  58. * @param br
  59. * @param encoding
  60. * @return
  61. * @throws IOException
  62. */
  63. private byte[] readBytes(BufferedReader br,String encoding) throws IOException{
  64. String str = null,retStr="";
  65. while ((str = br.readLine()) != null) {
  66. retStr += str;
  67. }
  68. if (StringUtils.isNotBlank(retStr)) {
  69. return retStr.getBytes(Charset.forName(encoding));
  70. }
  71. return null;
  72. }
  73. }

step 2:

添加 RepeatedlyReadFilter 实现 filter 过滤器接口方法,当客户端的请求先 过滤 进入SpringMvc Dispatch 路由前先包装下

  1. package com.filter;
  2.  
  3. import com.config.RepeatedlyReadRequestWrapper;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6.  
  7. import javax.servlet.*;
  8. import javax.servlet.http.HttpServletRequest;
  9. import java.io.IOException;
  10.  
  11. /**
  12. * 复制请求数据包body
  13. * 以提供 拦截器下 可数次获取Body数据包*/
  14. public class RepeatedlyReadFilter implements Filter {
  15.  
  16. private static final Logger logger = LoggerFactory.getLogger(RepeatedlyReadFilter.class);
  17.  
  18. @Override
  19. public void init(FilterConfig filterConfig) throws ServletException {
  20.  
  21. }
  22.  
  23. @Override
  24. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  25. logger.debug("复制request.getInputStream流");
  26. ServletRequest requestWrapper = null;
  27. if (request instanceof HttpServletRequest) {
  28. requestWrapper = new RepeatedlyReadRequestWrapper((HttpServletRequest) request);
  29. }
  30. if (null == requestWrapper) {
  31. chain.doFilter(request, response);
  32. } else {
  33. chain.doFilter(requestWrapper, response);
  34. }
  35. }
  36.  
  37. @Override
  38. public void destroy() {
  39.  
  40. }
  41. }

step 3:

添加拦截器 RepeatedlyReadInterceptor 继承 HandlerInterceptorAdapter 拦截适配器

  1. package com.interceptor;
  2.  
  3. import com.config.RepeatedlyReadRequestWrapper;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.web.servlet.ModelAndView;
  7. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  8.  
  9. import javax.servlet.ServletInputStream;
  10. import javax.servlet.ServletRequest;
  11. import javax.servlet.http.HttpServletRequest;
  12. import javax.servlet.http.HttpServletResponse;
  13. import java.io.*;
  14. import java.nio.charset.Charset;
  15.  
  16. public class RepeatedlyReadInterceptor extends HandlerInterceptorAdapter {
  17.  
  18. private static final Logger logger = LoggerFactory.getLogger(RepeatedlyReadInterceptor.class);
  19.  
  20. @Override
  21. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  22. /**
  23. * 对来自后台的请求统一进行日志处理
  24. */
  25. RepeatedlyReadRequestWrapper requestWrapper;
  26. if (request instanceof RepeatedlyReadRequestWrapper) {
  27. // 签名处理过程 start....
  28. requestWrapper = (RepeatedlyReadRequestWrapper) request;
  29. logger.info("请求Body: {} ", getBodyString(requestWrapper));
  30. // 签名处理过程 end....
  31. }
  32. // 默认记录后台接口请求日志记录
  33. String url = request.getRequestURL().toString();
  34. String method = request.getMethod();
  35. String uri = request.getRequestURI();
  36. String queryString = request.getQueryString();
  37. logger.info(String.format("请求参数, url: %s, method: %s, uri: %s, params: %s ", url, method, uri, queryString));
  38. return super.preHandle(request, response, handler);
  39. }
  40.  
  41. @Override
  42. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  43. RepeatedlyReadRequestWrapper requestWrapper;
  44. if (request instanceof RepeatedlyReadRequestWrapper) {
  45. // 测试再次获取Body start....
  46. requestWrapper = (RepeatedlyReadRequestWrapper) request;
  47. logger.info("请求Body: {} ", getBodyString(requestWrapper));
  48. // 测试再次获取Body end....
  49. }
  50. }
  51.  
  52. @Override
  53. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  54.  
  55. }
  56.  
  57. /**
  58. * 获取请求Body
  59. *
  60. * @param request
  61. *
  62. * @return
  63. */
  64. public static String getBodyString(final ServletRequest request) {
  65. StringBuilder sb = new StringBuilder();
  66. InputStream inputStream = null;
  67. BufferedReader reader = null;
  68. try {
  69. inputStream = cloneInputStream(request.getInputStream());
  70. reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
  71. String line = "";
  72. while ((line = reader.readLine()) != null) {
  73. sb.append(line);
  74. }
  75. } catch (IOException e) {
  76. e.printStackTrace();
  77. } finally {
  78. if (inputStream != null) {
  79. try {
  80. inputStream.close();
  81. } catch (IOException e) {
  82. e.printStackTrace();
  83. }
  84. }
  85. if (reader != null) {
  86. try {
  87. reader.close();
  88. } catch (IOException e) {
  89. e.printStackTrace();
  90. }
  91. }
  92. }
  93. return sb.toString();
  94. }
  95.  
  96. /**
  97. * Description: 复制输入流</br>
  98. *
  99. * @param inputStream
  100. *
  101. * @return</br>
  102. */
  103. public static InputStream cloneInputStream(ServletInputStream inputStream) {
  104. ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  105. byte[] buffer = new byte[1024];
  106. int len;
  107. try {
  108. while ((len = inputStream.read(buffer)) > -1) {
  109. byteArrayOutputStream.write(buffer, 0, len);
  110. }
  111. byteArrayOutputStream.flush();
  112. } catch (IOException e) {
  113. e.printStackTrace();
  114. }
  115. InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
  116. return byteArrayInputStream;
  117. }
  118. }

step 4:

配置过滤器与拦截器 WebMvcConfig

  1. package com.config;
  2.  
  3. import com.filter.RepeatedlyReadFilter;
  4. import com.interceptor.MyInterceptor;
  5. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.web.servlet.config.annotation.*;
  9.  
  10. /**
  11. * SpringMVC 配置类*/
  12. @Configuration
  13. public class WebMvcConfig extends WebMvcConfigurerAdapter {
  14.  
  15. @Override
  16. public void addInterceptors(InterceptorRegistry registry) {
  17. registry.addInterceptor(new RepeatedlyReadInterceptor()).addPathPatterns("/**");
  18. super.addInterceptors(registry);
  19. }
  20.  
  21. @Bean
  22. public FilterRegistrationBean repeatedlyReadFilter() {
  23. FilterRegistrationBean registration = new FilterRegistrationBean();
  24. RepeatedlyReadFilter repeatedlyReadFilter = new RepeatedlyReadFilter();
  25. registration.setFilter(repeatedlyReadFilter);
  26. registration.addUrlPatterns("/*");
  27. return registration;
  28. }
  29. }

拦截器中,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. mongodb增删改查常用命令总结

    前言 去年我还折腾过mongodb,后来用不到也就没碰了,这就导致了我忘的一干二净,不得不感叹,编程这东西只要不用,就会忘没了.现在我想重拾mongodb,来总结一下常用命令,主要就是增删改查. 另外 ...

  2. Java 将Maven项目打成可执行jar包

    一.用maven-shade-plugin打包 在pom.xml文件中加入如下信息,利用Maven的maven-shade-plugin插件进行打包. <build> <plugin ...

  3. Tarjan水题系列(5):最大半连通子图 [ZJOI2007 luogu P2272]

    题目 大意: 缩点后转为求最长链的长度和最长链的个数 思路: 看懂题就会做系列 长度和个数都可以拓扑排序后DP求得 毕竟是2007年的题 代码: 如下 #include <cstdio> ...

  4. 请求转发forward()和URL重定向redirect()的区别

  5. vue项目--vuex状态管理器

    本文取之官网和其他文章结合自己的理解用简单化的语言表达.用于自己的笔记记录,也希望能帮到其他小伙伴理解,学习更多的前端知识. Vuex 是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态 ...

  6. Flask开发系列之Flask+redis实现IP代理池

    Flask开发系列之Flask+redis实现IP代理池 代理池的要求 多站抓取,异步检测:多站抓取:指的是我们需要从各大免费的ip代理网站,把他们公开的一些免费代理抓取下来:一步检测指的是:把这些代 ...

  7. Core Graphics 定制UIVIew 处理图片

    许多UIView的子类,如UIButton或UILabel,它们的形状都是系统固定的.但是,对于一些特殊的情况,我们需要绘制产品狗想要的图形.那么等待我们的只有两个选择:第一,可以使用UIImageV ...

  8. Java Script入门

    学习来源:https://www.runoob.com/js/js-tutorial.html JavaScript 教程 JavaScript 是 Web 的编程语言. 所有现代的 HTML 页面都 ...

  9. 转载:利用php数组函数进行函数式编程

    因为一个BUG, 我在一个摇摇欲坠,几乎碰一下就会散架的项目中某一个角落中发现下面这样一段代码 这段程序与那个BUG有密切的关系. 我来回反复的捉摸这段代码, 发现这段代码实现了两个功能 第一个是在一 ...

  10. Service_Worker XSS

    0x00 简介 Service Worker 是 Chrome 团队提出和力推的一个 WEB API,用于给 web 应用提供高级的可持续的后台处理能力.该 WEB API 标准起草于 2013 年, ...