转载:http://blog.sina.com.cn/s/blog_870cd7b90101fg58.html

最近使用spring mvc做项目,数据格式是json,有一个功能是实现记录请求的参数,而请求的参数是整个RequestBody,Controller里是用过@RequestBody获取的。实现方法是通过一个Filter读取整个RequestBody并记录。但是这时就遇到一个问题,ServletRequest的getReader()和getInputStream()两个方法只能被调用一次,而且不能两个都调用。那么如果Filter中调用了一次,在Controller里面就不能再调用了。查看了下ServletRequest的说明,如下:

Java代码
  1. public ServletInputStream getInputStream() throws IOException;
  2. public BufferedReader getReader() throws IOException;

两个方法都注明方法只能被调用一次,由于RequestBody是流的形式读取,那么流读了一次就没有了,所以只能被调用一次。既然是因为流只能读一次的原因,那么只要将流的内容保存下来,就可以实现反复读取了。

实现方法:先将RequestBody保存为一个byte数组,然后通过Servlet自带的HttpServletRequestWrapper类覆盖getReader()和getInputStream()方法,使流从保存的byte数组读取。然后再Filter中将ServletRequest替换为ServletRequestWrapper。代码如下:

BodyReaderHttpServletRequestWrapper类包装ServletRequest,将流保存为byte[],然后将getReader()和getInputStream()方法的流的读取指向byte[]

Java代码
  1. import java.io.BufferedReader;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import javax.servlet.ServletInputStream;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletRequestWrapper;
  8. import jodd.JoddDefault;
  9. import jodd.io.StreamUtil;
  10. public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
  11. private final byte[] body;
  12. public BodyReaderHttpServletRequestWrapper(HttpServletRequest request)
  13. throws IOException {
  14. super(request);
  15. body = StreamUtil.readBytes(request.getReader(), JoddDefault.encoding);
  16. }
  17. @Override
  18. public BufferedReader getReader() throws IOException {
  19. return new BufferedReader(new InputStreamReader(getInputStream()));
  20. }
  21. @Override
  22. public ServletInputStream getInputStream() throws IOException {
  23. final ByteArrayInputStream bais = new ByteArrayInputStream(body);
  24. return new ServletInputStream() {
  25. @Override
  26. public int read() throws IOException {
  27. return bais.read();
  28. }
  29. };
  30. }
  31. }

在Filter中将ServletRequest替换为ServletRequestWrapper

Java代码
    1. public class HttpServletRequestReplacedFilter implements Filter {
    2. @Override
    3. public void init(FilterConfig filterConfig) throws ServletException {
    4. //Do nothing
    5. }
    6. @Override
    7. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
    8. ServletRequest requestWrapper = null;
    9. if(request instanceof HttpServletRequest) {
    10. requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
    11. }
    12. if(null == requestWrapper) {
    13. chain.doFilter(request, response);
    14. } else {
    15. chain.doFilter(requestWrapper, response);
    16. }
    17. }
    18. @Override
    19. public void destroy() {
    20. //Do nothing
    21. }
    22. }

ServletRequest中getReader()和getInputStream()只能调用一次的解决办法的更多相关文章

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

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

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

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

  3. 拦截器中,request中getReader()和getInputStream()只能调用一次,构建可重复读取inputStream的request.

    由于 request中getReader()和getInputStream()只能调用一次 在项目中,可能会出现需要针对接口参数进行校验等问题. 因此,针对这问题,给出一下解决方案 实现方法:先将Re ...

  4. es6 Object.assign ECMAScript 6 笔记(六) ECMAScript 6 笔记(一) react入门——慕课网笔记 jquery中动态新增的元素节点无法触发事件解决办法 响应式图像 弹窗细节 微信浏览器——返回操作 Float 的那些事 Flex布局 HTML5 data-* 自定义属性 参数传递的四种形式

    es6 Object.assign   目录 一.基本用法 二.用途 1. 为对象添加属性 2. 为对象添加方法 3. 克隆对象 4. 合并多个对象 5. 为属性指定默认值 三.浏览器支持 ES6 O ...

  5. c++调用动态库失败解决办法

    c++调用动态库失败解决办法 之前写好的程序今天早上过来发现在服务器上出错了,于是就各种查问题,整整一个早上外加下午两个小时都在查这个问题,最终被我找到了问题: 在程序中我发现LoadLibrary( ...

  6. Android中View类OnClickListener和DialogInterface类OnClickListener冲突解决办法

    Android中View类OnClickListener和DialogInterface类OnClickListener冲突解决办法 如下面所示,同时导入这两个,会提示其中一个与另一个产生冲突. 1i ...

  7. .NET在IE9中页面间URL传递中文变成乱码的解决办法

     在.Net的项目中,鼠标点击查询按钮,转到查询页面,但URL中包含中文时,传到服务器端后,中文变成了乱码(只有IE9出现该问题).       尝试使用Server.UrlEncode()进行编码, ...

  8. Visual studio 2017中 Javascript对于Xrm对象模型没有智能提示的解决办法

    Visual studio 2017中 Javascript对于Xrm对象模型没有智能提示的解决办法 先上个图.语法提示支持到 Microsoft Dynamics xRM API 8.2 也就是cr ...

  9. electron-vue中使用iview 报错this. is readonly的解决办法

    title: electron-vue中使用iview 报错this. is readonly的解决办法 toc: false date: 2019-02-12 19:33:28 categories ...

随机推荐

  1. 【Qt】Qt Assistant介绍【转】

    简介 Qt Assistant也就是我们常说的Qt助手,是一款用于呈现在线文档的工具. 简介 一分钟学会使用 Qt参考文档 Qt Assistant详解 命令行选项 工具窗口 文档窗口 工具栏 菜单 ...

  2. Linux 如何设置只允许域名访问站点而禁止IP访问站点

    最近在论坛里看到有人问到 Linux 如何设置只允许域名访问站点而禁止IP访问站点的问题,之前自己也用过这个功能,可以防止别人用 IP 地址来访问到自己的网站,下面我就我自己的环境给出解决方法,我用的 ...

  3. JavaScript 时间显示

    <span id="localtime"><span> <script type="text/javascript"> fu ...

  4. Python开发【第一篇】Python基础之反射

    反射 反射的作用:反射得作用是提高代码可读行. __import__导入模块和import导入模块的区别: __import__导入模块是通过字符串进行导入. import是常用得导入模块方法. 扩展 ...

  5. 基于 WebAPI 的 API 实现

    本文基于 WebAPI OData (微软发起的一个格式标准,其中一个比较有意思的是可以直接在 Excel 中填入 API 就可以展示了) Swashbuckle.OData(把 API 生成一个测试 ...

  6. 获得当前时间的PRO

    1.没有参数的存储过程 create or replace procedure get_timeas    cur_time varchar2(10);begin  select to_char(sy ...

  7. 【quartz】 理论知识

    属性的介绍 1.调度器属性:分别设置调度器的实例名(instanceName) 和实例 ID (instanceId).属性 org.quartz.scheduler.instanceName 可以是 ...

  8. 完整DataTable与IList互换(转)

    public class CollectionHelper { private CollectionHelper() { } public static DataTable ConvertTo< ...

  9. C++中的快速排序(使用vector和数组的不同)

    1.快速排序是最最基本的排序算法之一,时间复杂度是O(nlog2(n)) 基本思想:分治法+递归 假设key为该序列的第一个元素,从后往前遍历,找到第一个小于key值的元素,将该元素赋值给左边的起始值 ...

  10. oracle中事务处理

    事务用于保证数据的一致性,它由一组相关的dml语句组成,该组的dml语句要么全部成功,要么全部失败. 事务和锁 当执行事务操作时(dml语句),oracle会在被作用的表上加锁,防止其它用户改表的结构 ...