千家信息网

如何解决为了多次读取ServletInputStream引发的一系列问题

发表于:2024-11-20 作者:千家信息网编辑
千家信息网最后更新 2024年11月20日,本篇内容主要讲解"如何解决为了多次读取ServletInputStream引发的一系列问题",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何解决为了多次读
千家信息网最后更新 2024年11月20日如何解决为了多次读取ServletInputStream引发的一系列问题

本篇内容主要讲解"如何解决为了多次读取ServletInputStream引发的一系列问题",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何解决为了多次读取ServletInputStream引发的一系列问题"吧!

目录
  • 多次读取ServletInputStream引发的问题

    • 因为服务器和app之间传输方式是JSON

    • 于是写了下面的filter来让ServletInputStream来多次读取

    • 解决思路:自己解决

  • ServletInputStream重复读取问题

    • 直接贴代码了,亲测能用

多次读取ServletInputStream引发的问题

因为服务器和app之间传输方式是JSON

格式如下

{ head:nullbody:nulltoken:xxxxxxxxxxxxxxxxxxxxx}

所以想在服务端写个拦截器或者filter先读取token,来验证身份.但是如果前置拦截,那么在springMVC里的参数控制器就会读取不到,导致@RequestBody 这个注解的参数不起作用.

原因在于ServletInputStream已经在前置拦截器里读取过一次,在接下来的ArgumentHandler里读取的时候就不能再读了,导致取不到数据而无法赋值.

于是写了下面的filter来让ServletInputStream来多次读取

先写个request

public class MyHttpRequest extends HttpServletRequestWrapper { private static Logger log=Logger.getLogger(MyHttpRequest.class);    private  byte[] bytes;    /**     * @param request {@link javax.servlet.http.HttpServletRequest} object.     * @throws IOException      */    public MyHttpRequest(HttpServletRequest request) throws IOException {        super(request);        bytes= IOUtils.toByteArray( request.getInputStream());          }     @Override    public ServletInputStream getInputStream() throws IOException {        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);        return new DelegatingServletInputStream(byteArrayInputStream);    }    @Override    public BufferedReader getReader() throws IOException {     return new BufferedReader(new InputStreamReader(getInputStream()));    }

以上内容是先读取出来,然后放到一个byte[]里,以后每次拿流的时候就可以再新建一个关于Byte[]的流.

然后再写一个filter

public class MyFilter implements Filter{  @Override public void destroy() {     }  @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)   throws IOException, ServletException {  HttpServletRequest request=(HttpServletRequest) req;  MyHttpRequest myrequest=new MyHttpRequest(request);  chain.doFilter(myrequest, res);   }  @Override public void init(FilterConfig arg0) throws ServletException {   } }

在web.xml里配置一下,试试效果,非常不错.可以多次读取了,

但是,我去

在post请求下,request.getParamter这种东西又不起作用了.

原因在于我们重写的那个request是默认调用父类的super.getparamter方法,而super.getparamter又依赖于super.getInputStream,但是在我们重写的request里的构造方法里,第一句话就老request注入到super里,然后下一句话就把老的request的流给读了,导致我们调用super.getparameter的时候遇到了一个空的流。

解决思路:自己解决

package com.hrhs.jyj.filter; import java.io.BufferedReader;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;import java.util.ArrayList;import java.util.Enumeration;import java.util.HashMap;import java.util.List;import java.util.Map; import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper; import org.apache.commons.io.IOUtils;import org.apache.log4j.Logger;import org.springframework.mock.web.DelegatingServletInputStream; public class MyHttpRequest extends HttpServletRequestWrapper { private static Logger log = Logger.getLogger(MyHttpRequest.class); private byte[] bytes; private String body; private Map> map; private int readMap=0; private String queryString; /**  * @param request  *            {@link javax.servlet.http.HttpServletRequest} object.  * @throws IOException  */ public MyHttpRequest(HttpServletRequest request) throws IOException {  super(request);  bytes = IOUtils.toByteArray(request.getInputStream());  queryString = request.getQueryString();   }  @Override public ServletInputStream getInputStream() throws IOException {  final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);  return new DelegatingServletInputStream(byteArrayInputStream); }  @Override public BufferedReader getReader() throws IOException {  return new BufferedReader(new InputStreamReader(getInputStream())); }  @Override public String getParameter(String name) {  log.info("走了getParameter");  return super.getParameter(name); }  @Override public Map getParameterMap() {  log.info("走了getParameterMap");  return super.getParameterMap(); }  @Override public Enumeration getParameterNames() {  log.info("走了getParameterNames");  return super.getParameterNames(); } //暂时先改写这一个,其他的也可以修改 @Override public String[] getParameterValues(String name) {  log.info("走了getParameterValues");  try {   Map> nameVals = doParameter();   List list = nameVals.get(name);   if(list!=null&&list.size()>0){    return list.toArray(new String[]{});   }  } catch (UnsupportedEncodingException e) {   e.printStackTrace();  }  return new String[]{}; } //这里获取所有参数值的map public Map> doParameter() throws UnsupportedEncodingException {  if(readMap==0){   //这里把post里的参数和地址栏参数结合到一起,然后解析   body = new String(bytes, getCharacterEncoding())+"&"+queryString;   String[] nameVals = body.split("&");   map = new HashMap>();   for (String nameVal : nameVals) {    String name = nameVal.split("=")[0];    String val = nameVal.split("=")[1];    if (map.containsKey(name)) {     List vals = map.get(name);     vals.add(val);     map.put(name, vals);    } else {     List vals = new ArrayList();     vals.add(val);     map.put(name, vals);    }   }   readMap=1;  }  return map;   }}

ServletInputStream重复读取问题

本来是想实现tomcat的acess_log打印post请求参数。吐槽一下,tomcat功能和nginx差了好几条街。网上找了个方法,用tomcat的filter来实现。

但是,写filter的时候,发现了ServletInputStream重复读取问题。

网上找个几个方法,都不能直接用。综合网上的资料,根据自己的理解,终于能完美运行了。

直接贴代码了,亲测能用

首先要写个BufferHttpServletRequestWrapper类,用来复制HttpServletRequest request。

import java.io.BufferedReader;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader; import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper; public class BufferHttpServletRequestWrapper extends HttpServletRequestWrapper {  private final byte[] body;  public BufferHttpServletRequestWrapper(HttpServletRequest request) throws IOException {  super(request);  InputStream is = request.getInputStream();        ByteArrayOutputStream baos = new ByteArrayOutputStream();        byte buff[] = new byte[ 1024 ];        int read;        while( ( read = is.read( buff ) ) > 0 ) {            baos.write( buff, 0, read );        }        body = baos.toByteArray(); }  @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();   }  }; } }

然后filter实现如下:

import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;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; /** * Servlet Filter implementation class PostDataDumperFilter */ public class PostDataDumperFilter implements Filter {  private FilterConfig filterConfig = null;     /**     * Default constructor.      */    public PostDataDumperFilter() {        // TODO Auto-generated constructor stub    }  /**  * @see Filter#destroy()  */ public void destroy() {  this.filterConfig = null;  }  /**  * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)  */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  if (filterConfig == null)    return;  //备份HttpServletRequest  ServletRequest requestWrapper = null;          if(request instanceof HttpServletRequest) {              requestWrapper = new BufferHttpServletRequestWrapper((HttpServletRequest) request);          }  //使用流  InputStream reader = requestWrapper.getInputStream();  ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(100);  int i =0;  byte [] b = new byte[100];  while((i = reader.read(b))!= -1){   byteOutput.write(b, 0, i);  }  request.setAttribute("post", new String(byteOutput.toByteArray()));  // pass the request along the filter chain  if(null == requestWrapper){   chain.doFilter(request, response);  } else {              chain.doFilter(requestWrapper, response);          } }  /**  * @see Filter#init(FilterConfig)  */ public void init(FilterConfig fConfig) throws ServletException {  this.filterConfig = fConfig; } }

到此,相信大家对"如何解决为了多次读取ServletInputStream引发的一系列问题"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0