天天看点

解决HttpServletRequest输入流只能读一次的问题

为什么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 {
 
    }
 
}