解决在Filter中读取Request中的流后,后续controller或restful接口中无法获取流的问题
来源:互联网 发布:优化推广 编辑:程序博客网 时间:2024/06/05 15:17
首先我们来描述一下在开发中遇到的问题,场景如下:
比如我们要拦截所有请求,获取请求中的某个参数,进行相应的逻辑处理:比如我要获取所有请求中的公共参数 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")); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public 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串 转换为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); } }}@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接口中无法获取流的问题
- 解决filter拦截request中body内容后,字符流关闭,无法传到controller的问题
- 拦截器中读取request中的流后,controller 无法获取到数据解决方案
- 解决在Filter中读取Request中的流后, 然后再Control中读取不到的做法
- 解决在Filter中读取Request中的流后, 然后再Control中读取不到的做法
- 解决微信小程序中在其他页面无法使用app.js中获取的userInfo或openid的问题
- SpringBoot拦截器或过滤器中使用流读取参数后,controller中注解读取不到参数
- directshow filter注册后无法在graphedit中显示的问题
- 在controller中获取不到的session值中的验证码问题
- 通过android:descendantFocusability解决listview每一项或项中view无法获取焦点的问题
- 通过JS向表单中增加元素后提交无法获取数据问题的解决。
- HttpServletRequestWrapper Filter中装饰Request 解决全站编码问题!
- 解决在virtualbox中ubuntu server安装后无法启动的问题
- 解决在virtualbox中ubuntu server安装后无法启动的问题(续……)
- 解决.net 2.0以上版本GDI+绘图保存后在Photoshop中无法识别的问题
- 解决ibus在google chrome / chromium 浏览器中使用Backspace后无法输入文字的问题
- 解决在eclipse中删除tomcat server后导致无法重新添加服务器的问题
- 关于在servlet中创建了cookie,但在jsp中无法读取到该cookie的问题(已解决)
- VS2010与QT的集成开发环境
- 使用Eclipse构建Maven的SpringMVC项目
- PHP中is_file,file_exists的区别,is_file 不能替代file_exits的理由
- Android编译系统详解(二)
- Android studio 插件
- 解决在Filter中读取Request中的流后,后续controller或restful接口中无法获取流的问题
- 一路追踪,追到git
- BSP-Apache.HAMA运行过程(框架)
- Android中BindService方式使用的理解
- Android 隐藏App图标和Home任务栈
- Android Service 双进程常驻后台(2)
- leveldb源码阅读知识积累4
- qt-creator源码研究2
- iOS中的私有属性问题