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

比如我们要拦截所有请求,获取请求中的某个参数,进行相应的逻辑处理:比如我要获取所有请求中的公共参数 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

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 { @Override
public void destroy() { } @Override
public 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串 转换为Map
if(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);
}
}
} @Override
public 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. 如何在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. abp(net core)+easyui+efcore实现仓储管理系统——使用 WEBAPI实现CURD (十四)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...

  2. 曹工杂谈:Java 类加载还会死锁?这是什么情况?

    一.前言 今天事不是很多,正好在Java交流群里,看到一个比较有意思的问题,于是花了点时间研究了一下,这里做个简单的分享. 先贴一份测试代码,大家可以先猜测一下,执行结果会是怎样的: import j ...

  3. python3 how to creat alphabet

    Python: How To Generate a List Of Letters In The Alphabet  Method 1# First we need to figure out the ...

  4. Mysql高可用(HA)

    MySQL特点: 1) 开放的源代码的关系型数据库 2) 适应于所有平台 3) 支持多线程,充分利用CPU资源,性能很出色 4) 价格便宜 5) 大数据库能处理5000万条记录. ACID 事务 一组 ...

  5. 浅谈UART/12C/TTL的定义与区别与解析

    UART/12C/TTL的定义与区别: UART:UART(Universal Asynchronous Receive Transmitter):也就是我们经常所说的串口,基本都用于调试.主机和从机 ...

  6. v语言怎么玩

    直接上github: https://github.com/vlang/v 前戏 大概是在6月份的时候,在github上看到了这个玩意,我以为是??? 我下意识的去查了一下有没有人在讨论这个语言,但是 ...

  7. idea打包报错

    There is insufficient memory for the Java Runtime Environment to continue.# Native memory allocation ...

  8. 企查查app新增企业数据抓取

    企查查每日新增企业数据抓取尚未完成的工作: 需要自行抓包获取设备id,appid,sign等等 sign和时间戳保持一致即可 把所有的数据库.redis配置 无法自动登录,账号需要独立 redis数据 ...

  9. deepin 15.11 成功安装 jupyter notebook

    系统环境: OS:deepin 15.11(均为系统默认配置) Anaconda Distribution 64位(x86)安装程序(517 MB) Jupyter 官方提供三种安装方式:conda. ...

  10. Docker学习总结(五)--迁移与备份

    将容器保存为镜像 docker commit myNginx mynginx_i 镜像备份 docker save -o myNginx.tar myNginx_i 镜像恢复 docker load ...