首先我们来描述一下在开发中遇到的问题,场景如下:

比如我们要拦截所有请求,获取请求中的某个参数,进行相应的逻辑处理:比如我要获取所有请求中的公共参数 token,clientVersion等等;这个时候我们通常有两种做法

前提条件是我们实现Filter类,重写doFilter方法

1、通过getParameter方法获得

HttpServletRequest hreq = (HttpServletRequest) req;

String param = hreq.getParameter("param");

这种方法有缺陷:它只能获取  POST 提交方式中的

  Content-Type: application/x-www-form-urlencoded;

这种提交方式中,key为param ,若提交类型不是这种方式就获取不到param的值

2、获取请求对象中的数据流,通过解析流信息来获取提交的内容;代码示例如下:

 /**
* 获取请求Body
*
* @param request
* @return
*/
public static String getBodyString(ServletRequest request) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = 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();
}

通过这种简单的解析方式,将http请求中的body解析成 字符串的形式;然后再对字符串的格式进行业务解析;

这种处理方式的优点是:能解析 出content-Type 为  application/x-www-form-urlencoded ,application/json , text/xml 这三种提交方式的 数据 (至于另外一种带文件的请求:multipart/form-data ,本人没有亲自测试过,等后期遇到此类为题再来解决)。 上述方法返回的可能是key,value(对应第一种content-type),可能是json字符串(对应第二种),可能是xml的字符串(对应第三种)

但是这种方式有一个很大的缺点:那就是当从请求中获取流以后,流被filter中的这个 inputStreamToString(InputStream in) 这个方法处理后就被“消耗”了,这会导致,chain.doFilter(request, res)这个链在传递 request对象的时候,里面的请求流为空,导致责任链模式下,其他下游的链无法获取请求的body,从而导致程序无法正常运行,这也使得我们的这个filter虽然可以获取请求信息,但是它会导致整个应用程序不可用,那么它也就失去了意义;

针对第二种方式的缺陷:流一旦被读取,就无法向下传递整个问题,有如下解决方案;

解决思路如下:将取出来的字符串,再次转换成流,然后把它放入到新request 对象中,在chain.doFiler方法中 传递新的request对象;要实现这种思路,需要自定义一个类

继承HttpServletRequestWrapper,然后重写public BufferedReader getReader()方法,public  ServletInputStream getInputStream()方法;(这两个方法的重写实现逻辑如下:getInputStream()方法中将body体中的字符串转换为字节流(它实质上返回的是一个ServletInputStream 对象);然后通过getReader()调用---->getInputStream()方法;),继承实现重写逻辑以后,在自定义分filter(VersionCheckFilter)中,使用自定义的HttpServletRequestWrapper(BodyReaderHttpServletRequestWrapper)将原始的HttpServletRequest对象进行再次封装;再通过BodyReaderHttpServletRequestWrapper对象去做dofilter(req,res)的req对象;

涉及的三个类的代码如下:

自定义的HttpServletRequestWrapper

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;import com.yt.util.HttpHelper;public class BodyReaderHttpServletRequestWrapper extendsHttpServletRequestWrapper {private final byte[] body;public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {super(request);System.out.println("-------------------------------------------------");  Enumeration e = request.getHeaderNames()   ;  while(e.hasMoreElements()){  String name = (String) e.nextElement();  String value = request.getHeader(name);  System.out.println(name+" = "+value);  }  body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream bais = new ByteArrayInputStream(body);return new ServletInputStream() {@Overridepublic int read() throws IOException {return bais.read();}};}@Overridepublic String getHeader(String name) {return super.getHeader(name);}@Overridepublic Enumeration<String> getHeaderNames() {return super.getHeaderNames();}@Overridepublic Enumeration<String> getHeaders(String name) {return super.getHeaders(name);}}

2、辅助类

HttpHelper

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;import javax.servlet.ServletRequest;public class HttpHelper {/*** 获取请求Body** @param request* @return*/public static String getBodyString(ServletRequest request) {StringBuilder sb = new StringBuilder();InputStream inputStream = null;BufferedReader reader = null;try {inputStream = 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();}
}

3、自定义的filter

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Map;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.crypto.URIDereferencer;import com.yt.util.HttpHelper;
import com.yt.util.JsonHelper;public class VersionCheckFilter implements Filter {@Overridepublic void destroy() {}@Overridepublic void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {HttpServletRequest hreq = (HttpServletRequest) req;String uri = hreq.getRequestURI();if(uri.contains("uploadImageServlet")){//图像上传的请求,不做处理chain.doFilter(req, res);}else{String reqMethod = hreq.getMethod();if("POST".equals(reqMethod)){PrintWriter out = null; HttpServletResponse response = (HttpServletResponse) res;response.setCharacterEncoding("UTF-8");  response.setContentType("application/json; charset=utf-8");  /* String requestStr = this.inputStreamToString(hreq.getInputStream());String[] arrs = requestStr.split("&"); for (String strs : arrs) {String[] strs2 = strs.split("=");for (int i = 0; i < strs2.length; i++) {if (strs2[0].equals("param")) {if (strs2[1] != null &&!strs2[1].equals("")) {System.out.println("test=" + strs2[1]);}}}}*/ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(hreq);String body = HttpHelper.getBodyString(requestWrapper);//如果是POST请求则需要获取 param 参数String param = URLDecoder.decode(body,"utf-8");//String param = hreq.getParameter("param");//json串 转换为Mapif(param!=null&¶m.contains("=")){param = param.split("=")[1];}Map paramMap = JsonHelper.getGson().fromJson(param, Map.class);Object obj_clientversion = paramMap.get("clientVersion");String clientVersion = null;if(obj_clientversion != null){clientVersion = obj_clientversion.toString();System.out.println(clientVersion);/*try {  out = response.getWriter();Map remap = new HashMap<String, Object>();remap.put("code", 9);remap.put("message", Constant.SYSTEM_ERR_DESC);out.append(JsonHelper.getGson().toJson(remap));  } catch (IOException e) {  e.printStackTrace();  } finally {  if (out != null) {  out.close();  }  }*/chain.doFilter(requestWrapper, res);  }else{chain.doFilter(requestWrapper, res);  }}else{//get请求直接放行chain.doFilter(req, res);}}}@Overridepublic void init(FilterConfig arg0) throws ServletException {}/*public String inputStreamToString(InputStream in) throws IOException {StringBuffer out = new StringBuffer();byte[] b = new byte[4096];for (int n; (n = in.read(b)) != -1;) {out.append(new String(b, 0, n));}return out.toString();}*/
}

解决在Filter中读取Request中的流后,后续controller或restful接口中无法获取流的问题相关推荐

  1. springmvc框架下Filter过滤器中过滤文件后 后续 controller为空的问题

    开发过程中遇到过滤文件类型的问题,需要校验文件的头信息,,在过滤器中通过request获取文件流信息  但是后续的controller中的MultipartFile一直都是空的,网上的网友的方案使用了 ...

  2. python opencv 实现从一个文件夹中读取图片做切割处理后放入另一个文件夹

    python opencv 实现从一个文件夹中读取图片切割处理后放入另一个文件夹. 实现的功能是把一个文件夹里的图片作处理,即把原图片中心为起点切割成1536*1536的图片,原图片必须大于这个的大小 ...

  3. java 从excel中读取数据_在Java中读取Excel文件的内容和导出数据到Excel文件中

    转自www.chianjavaworld.net 原作者:SonyMusic 读:rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr 在Java ...

  4. java中 关于静态方法的定义_为什么我们不能在Java接口中定义静态方法?

    从Java 8开始,Java接口中允许使用静态方法. 从Java 8开始,接口也可以具有静态帮助器方法.public interface vehicle { default void print()  ...

  5. 3行代码实现从excel中读取出某列元素为所想要的元素集合中的所有行

    下面代码实现了,读取脚本所在目录的名字叫做filename的一个excel文件.并特定的只选了其中columns这一列元素在Values中的行 代码在下面 def readexcel(filename ...

  6. .net 从txt中读取行数据_【VBA项目】从指定文件中读取数据并绘制图表

    VBA 是一种很久远的编程语言,但并不过时.在满足以下两个条件时,借助 VBA 可以极大的提升生产率,降低出错率: 你的电脑上不允许自主安装软件: 你需要执行的工作中大部分的步骤都是固定且重复的. 项 ...

  7. c语言从txt中读取二维坐标,C语言二维数组在文件中读写的问题,谢谢

    已结贴√ 问题点数:10 回复次数:4 C语言二维数组在文件中读写的问题,谢谢 这是一个用二位数组写的五子棋小游戏的代码,我的思路是通过键盘输入坐标显示棋子,当输0 0时保存棋盘并结束游戏,下一次进入 ...

  8. element中upload单图片转base64后添加进数组,请求接口

    //先上代码 <template> <!-- data绑定的参数getuploadloge: [{url: '',name: ''}],--> <!-- 编辑操作模板th ...

  9. PCB 电测试--测试点数自动输出到流程指示中(读取TGZ Stephdr文件)

    好不容易实现了 <PCB 无需解压,直接读取Genesis TGZ指定文件 > 正好这里有一项需求:PCB电测试--测试点数自动输出到流程指示中 一.自动输出测试点小结; 1.由于历史原因 ...

最新文章

  1. linux基础(6)-shell编程
  2. python精要(71)-VMDK操作(1)
  3. 决策树可视化,被惊艳到了!
  4. excel表格中添加combobox_excel高阶打印技巧:批量为表格添加logo,学会它老板都夸你厉害...
  5. 博弈论(一)基本概念
  6. Ubuntu下hadoop2.4搭建集群(单机模式)
  7. 深度内幕丨揭秘积分墙新颖反作弊
  8. java多个文件压缩成zip文件
  9. 电容或电感的电压_电容和电感的区别、电压超前电流、电流超前电压,傻傻分不清楚...
  10. 张亮:Sharding-Sphere成长记
  11. 公路自行车入门级推荐java_想玩公路自行车,有没有入门推荐?
  12. ai旋转扭曲_【AI~扭曲】旋转扭曲、收拢、膨胀、扇贝、晶格化、皱褶工具(转)...
  13. C语言中三个数比较大小详解——三种方法
  14. PS新手教程!五分钟绘制一张半调效果的耐克体育海报
  15. NLP预训练(PTMs)
  16. Could not open Hibernate Session for transaction; nested exception is org.hibernate.exception.Generi
  17. 利用压缩文件修改加密word文档
  18. C/C++黑魔法-三字母彩蛋
  19. 自由软件许可证GPL
  20. 内存检测工具BoundsChecker使用详述

热门文章

  1. 戴尔Precision M6800加装西部数据NVMESSD-SN52固态硬盘 装4G模块
  2. 在ios上运行安卓计算机软件,安卓竟然也能运行iOS,苹果怒了!
  3. 我,27岁,数据分析师,今年无情被辞:想给数据人提个醒!!
  4. fortran教程9:和C语言混合编程
  5. ZT——你怎么过河? -在CMM实践中你是否愿意多走1公里-软件工程 CMM与过程改进
  6. mysql截断表_mysql – 如何正确截断表?
  7. Kubernetes k8s理论篇
  8. 输出全靠画html5在线玩4399,输出全靠画
  9. c语言求最大值 若有多个最大,二个随机变量的最大值与最小值分布的求法.pdf
  10. ALEXA中国网站排名真相调查[内幕调查:出卖Alexa]