为什么HttpServletRequest的输入流只能读一次呢?
当我们调用getInputStream()
方法获取输入流时得到的是一个InputStream对象,而实际类型是ServletInputStream,它继承于InputStream。
InputStream的read()
方法内部有一个postion,标志当前流被读取到的位置,每读取一次,该标志就会移动一次,如果读到最后,read()
会返回-1,表示已经读取完了。如果想要重新读取则需要调用reset()
方法,position就会移动到上次调用mark的位置,mark默认是0,所以就能从头再读了。调用reset()
方法的前提是已经重写了reset()
方法,当然能否reset也是有条件的,它取决于markSupported()
方法是否返回true。
InputStream默认不实现reset的相关方法,而ServletInputStream也没有重写reset的相关方法,这样就无法重复读取流,这就是我们从request对象中获取的输入流就只能读取一次的原因。
解决办法:重写HttpServletRequestWrapper方法
通过重写HttpServletRequestWrapper把request的保存下来,然后通过过滤器保存下来的request在填充进去,这样就可以多次读取request了。
重写HttpServletRequestWrapper
package com.aliyun.icc.core.config.httpHelp; 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.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); 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 boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } @Override public int read() throws IOException { return bais.read(); } }; } @Override public String getHeader(String name) { return super.getHeader(name); } @Override public Enumeration<String> getHeaderNames() { return super.getHeaderNames(); } @Override public Enumeration<String> getHeaders(String name) { return super.getHeaders(name); } }
再编写过滤器
package com.aliyun.core.config.filter; import com.aliyun.core.config.httpHelp.BodyReaderHttpServletRequestWrapper; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; public class HttpServletRequestReplacedFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { ServletRequest requestWrapper = null; if(request instanceof HttpServletRequest && (null == request.getParameterMap() || request.getParameterMap().isEmpty())) { requestWrapper = new RequestReaderHttpServletRequestWrapper((HttpServletRequest) request); } //获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。 // 在chain.doFiler方法中传递新的request对象 if(requestWrapper == null) { chain.doFilter(request, response); } else { chain.doFilter(requestWrapper, response); } } @Override public void init(FilterConfig arg0) throws ServletException { } }