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

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

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

1、通过getParameter方法获得

HttpServletRequest hreq = (HttpServletRequest) req;

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

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

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

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

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

  1. /**
  2. * 获取请求Body
  3. *
  4. * @param request
  5. * @return
  6. */
  7. public static String getBodyString(ServletRequest request) {
  8. StringBuilder sb = new StringBuilder();
  9. InputStream inputStream = null;
  10. BufferedReader reader = null;
  11. try {
  12. inputStream = request.getInputStream();
  13. reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
  14. String line = "";
  15. while ((line = reader.readLine()) != null) {
  16. sb.append(line);
  17. }
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. } finally {
  21. if (inputStream != null) {
  22. try {
  23. inputStream.close();
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28. if (reader != null) {
  29. try {
  30. reader.close();
  31. } catch (IOException e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. }
  36. return sb.toString();
  37. }

通过这种简单的解析方式,将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

2、辅助类

HttpHelper

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.InputStreamReader;
  5. import java.nio.charset.Charset;
  6.  
  7. import javax.servlet.ServletRequest;
  8.  
  9. public class HttpHelper {
  10. /**
  11. * 获取请求Body
  12. *
  13. * @param request
  14. * @return
  15. */
  16. public static String getBodyString(ServletRequest request) {
  17. StringBuilder sb = new StringBuilder();
  18. InputStream inputStream = null;
  19. BufferedReader reader = null;
  20. try {
  21. inputStream = request.getInputStream();
  22. reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
  23. String line = "";
  24. while ((line = reader.readLine()) != null) {
  25. sb.append(line);
  26. }
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. } finally {
  30. if (inputStream != null) {
  31. try {
  32. inputStream.close();
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. if (reader != null) {
  38. try {
  39. reader.close();
  40. } catch (IOException e) {
  41. e.printStackTrace();
  42. }
  43. }
  44. }
  45. return sb.toString();
  46. }
  47. }

3.自定义的filter

  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.io.PrintWriter;
  4. import java.net.URL;
  5. import java.net.URLDecoder;
  6. import java.util.Map;
  7.  
  8. import javax.servlet.Filter;
  9. import javax.servlet.FilterChain;
  10. import javax.servlet.FilterConfig;
  11. import javax.servlet.ServletException;
  12. import javax.servlet.ServletRequest;
  13. import javax.servlet.ServletResponse;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. import javax.xml.crypto.URIDereferencer;
  17.  
  18. import com.yt.util.HttpHelper;
  19. import com.yt.util.JsonHelper;
  20.  
  21. public class VersionCheckFilter implements Filter {
  22.  
  23. @Override
  24. public void destroy() {
  25.  
  26. }
  27.  
  28. @Override
  29. public void doFilter(ServletRequest req, ServletResponse res,
  30. FilterChain chain) throws IOException, ServletException {
  31. HttpServletRequest hreq = (HttpServletRequest) req;
  32. String uri = hreq.getRequestURI();
  33. if(uri.contains("uploadImageServlet")){
  34. //图像上传的请求,不做处理
  35. chain.doFilter(req, res);
  36. }else{
  37. String reqMethod = hreq.getMethod();
  38. if("POST".equals(reqMethod)){
  39.  
  40. PrintWriter out = null;
  41. HttpServletResponse response = (HttpServletResponse) res;
  42. response.setCharacterEncoding("UTF-8");
  43. response.setContentType("application/json; charset=utf-8");
  44.  
  45. /* String requestStr = this.inputStreamToString(hreq.getInputStream());
  46.  
  47. String[] arrs = requestStr.split("&");
  48. for (String strs : arrs) {
  49. String[] strs2 = strs.split("=");
  50. for (int i = 0; i < strs2.length; i++) {
  51. if (strs2[0].equals("param")) {
  52. if (strs2[1] != null &&!strs2[1].equals("")) {
  53. System.out.println("test=" + strs2[1]);
  54. }
  55. }
  56. }
  57. }*/
  58.  
  59. ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(hreq);
  60. String body = HttpHelper.getBodyString(requestWrapper);
  61.  
  62. //如果是POST请求则需要获取 param 参数
  63. String param = URLDecoder.decode(body,"utf-8");
  64. //String param = hreq.getParameter("param");
  65. //json串 转换为Map
  66. if(param!=nullm.contains("=")){
  67. param = param.split("=")[1];
  68. }
  69. Map paramMap = JsonHelper.getGson().fromJson(param, Map.class);
  70. Object obj_clientversion = paramMap.get("clientVersion");
  71.  
  72. String clientVersion = null;
  73. if(obj_clientversion != null){
  74. clientVersion = obj_clientversion.toString();
  75. System.out.println(clientVersion);
  76. /*try {
  77. out = response.getWriter();
  78. Map remap = new HashMap<String, Object>();
  79. remap.put("code", 9);
  80. remap.put("message", Constant.SYSTEM_ERR_DESC);
  81. out.append(JsonHelper.getGson().toJson(remap));
  82. } catch (IOException e) {
  83. e.printStackTrace();
  84. } finally {
  85. if (out != null) {
  86. out.close();
  87. }
  88. }*/
  89. chain.doFilter(requestWrapper, res);
  90. }else{
  91. chain.doFilter(requestWrapper, res);
  92. }
  93. }else{
  94. //get请求直接放行
  95. chain.doFilter(req, res);
  96. }
  97. }
  98. }
  99.  
  100. @Override
  101. public void init(FilterConfig arg0) throws ServletException {
  102.  
  103. }
  104.  
  105. /*public String inputStreamToString(InputStream in) throws IOException {
  106. StringBuffer out = new StringBuffer();
  107. byte[] b = new byte[4096];
  108. for (int n; (n = in.read(b)) != -1;) {
  109. out.append(new String(b, 0, n));
  110. }
  111. return out.toString();
  112. }*/
  113. }

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

  1. 如何在ASP.NET Core自定义中间件中读取Request.Body和Response.Body的内容?

    原文:如何在ASP.NET Core自定义中间件中读取Request.Body和Response.Body的内容? 文章名称: 如何在ASP.NET Core自定义中间件读取Request.Body和 ...

  2. ASP.NET Core 2.0 中读取 Request.Body 的正确姿势

    原文:ASP.NET Core 中读取 Request.Body 的正确姿势 ASP.NET Core 中的 Request.Body 虽然是一个 Stream ,但它是一个与众不同的 Stream ...

  3. ASP.NET Core 5.0 中读取Request中Body信息

    ASP.NET Core 5.0 中读取Request中Body信息 记录一下如何读取Request中Body信息 public class ValuesController : Controller ...

  4. 解决在Filter中读取Request中的流后, 然后再Control中读取不到的做法

    摘要: 大家知道, StringMVC中@RequestBody是读取的流的方式, 如果在之前有读取过流后, 发现就没有了. 我们来看一下核心代码: filter中主要做的事情, 就是来校验请求是否合 ...

  5. ASP.NET Core 中读取 Request.Body 的正确姿势

    ASP.NET Core 中的 Request.Body 虽然是一个 Stream ,但它是一个与众不同的 Stream —— 不允许 Request.Body.Position=0 ,这就意味着只能 ...

  6. .NET Core 中读取 Request.Headers 的姿势

    Request.Headers 的类型是 IHeaderDictionary 接口,对应的实现类是 HeaderDictionary ,C# 实现源码见 HeaderDictionary.cs . H ...

  7. 每日踩坑 2018-09-29 .Net Core 控制器中读取 Request.Body

    测试代码: 结果: PostMan: 代码: private string GetRequestBodyUTF8String() { this.Request.EnableBuffering(); ; ...

  8. 【API管理 APIM】如何查看APIM中的Request与Response详细信息,如Header,Body中的参数内容

    问题描述 通过APIM门户或者是Developer门户,我们可以通过Test功能测试某一个接口,通过Trace可以获取非常详细的Request,Response的信息,包含Header,X-Forwa ...

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

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

随机推荐

  1. Zookeeper_阅读源码第一步_在 IDE 里启动 zkServer(单机版)

    Zookeeper是开源的,如果想多了解Zookeeper或看它的源码,最好是能找到它的源码并在 IDE 里启动,可以debug看它咋执行的,能够帮助你理解其原理. 准备源码 所以我们很容易搞到它的源 ...

  2. c#Winform自定义控件-目录

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...

  3. monkey学习总结笔记

    一.什么是monkey? Monkey是Android中的一个命令行工具,可以运行在模拟器里或实际设备中.它向系统发送伪随机的用户事件流(如按键输入.触摸屏输入.手势输入等),实现对正在开发的应用程序 ...

  4. 如何使用WorkManager执行后台任务(下)

    0x00 WorkManager的高级用法 在上一文中已经了解到 WorkManager的基本用法之后,今天来看看它的一些高级用法: 链式任务调用 唯一任务序列 传递参数和获取返回值 0x01 链式任 ...

  5. Python模块之requests,urllib和re

    目录 一.爬虫的步骤 二.使用Jupyter 三.爬虫请求模块之urllib 四.爬虫请求模块之requests 五.爬虫分析之re模块 一.爬虫的步骤 1.发起请求,模拟浏览器发送一个http请求 ...

  6. MySQL多表关联数据同时删除

    MySQL多表关联时的多表删除: DELETE t1, t2FROM    t1LEFT JOIN t2 ON t1.id = t2.idWHERE    t1.id = 25

  7. 从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之四Nlog记录日志至数据库

    为什么要进行日志记录呢?为什么要存至数据库呢?只能说日志记录是每个系统都应当有的. 好的日志记录方式可以提供我们足够多定位问题的依据.查找系统或软件或项目的错误或异常记录.程序在运行时就像一个机器人, ...

  8. net core WebApi——文件分片下载

    目录 前言 开始 测试 小结 @ 前言 上一篇net core WebApi--文件分片上传与跨域请求处理介绍完文件的上传操作,本来是打算紧接着写文件下载,中间让形形色色的事给耽误的,今天还是抽个空整 ...

  9. ionic app 优化三件套,让其更贴近原生app

    这里推荐一个ionic大神的简书,里面有好多关于好多ionic的技术分享! http://www.jianshu.com/u/c2e637a941ef 捣鼓了好久的ionic,终于在优化过程终于有所进 ...

  10. Angular Material 的设计之美

    前言 Angular Material 作为 Angular 的官方组件库,无论是设计交互还是易用性都有着极高的质量.正如官方所说其目的就是构建基于 Angular 和 Typescript 的高质量 ...