getInputStream/getReader() has already been called for this request

getInputStream/getReader() has already been called for this request

一、背景

定义了一个安全校验过滤器,现在要获取请求参数。

  • 通过request.getParameter获取请求参数,然而这种方式只能获取POST方式中的Content-Type: application/x-www-form-urlencoded。

  • 通过request.getInputStream(或者getReader)读取请求数据流(能解析出content-Type为 application/x-www-form-urlencoded ,application/json , text/xml这三种提交方式的数据(注:multipart/form-data不行),但是!!!在后续controller接口中无法获取该数据流

二、原因

The Servlet API documentation for getReader() and getInputStream() says:

public java.io.BufferedReader getReader()
    Throws:
    java.lang.IllegalStateException - if getInputStream() method has been called on this request

 public ServletInputStream getInputStream()
    Throws:
    java.lang.IllegalStateException - if the getReader() method has already been called for this request

主要问题在于:不能在过滤器中读取一次二进制流(字符流),又在另外一个Servlet中读取一次,即一个InputSteam(BufferedReader)对象在被读取完成后,将无法再次被读取。

三、解决方案

使用MyRequestWrapper来包装HttpServletRequest,在MyRequestWrapper中初始化读取request的BufferedReader数据,以String形式缓存在其中,然后在Filter中将request转换为包装过的request(即克隆请求的思想)

public class MyRequestWrapper extends HttpServletRequestWrapper {

    private final String body;

    public MyRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);

        StringBuilder sb = new StringBuilder();
        String line;
        BufferedReader reader = request.getReader();
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }

        body = sb.toString();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        ServletRequest myRequestWrapper = null;

        Map<String, String> params = new HashMap<>(16);
        // 封装请求参数
        switch (request.getMethod()) {
            case BaseConsts.HttpConts.METHOD_POST:
                myRequestWrapper = new MyRequestWrapper(request);
                JSONObject jsonObject = JSONObject.parseObject(myRequestWrapper.getBody());
                for (String key : jsonObject.keySet()) {
                    params.put(key, jsonObject.get(key).toString());
                }
                break;
            case BaseConsts.HttpConts.METHOD_GET:
                Enumeration<String> pNames = request.getParameterNames();
                while (pNames.hasMoreElements()) {
                    String pName = pNames.nextElement();
                    params.put(pName, request.getParameter(pName));
                }
                break;
            default:
                break;
        }

        CheckReult checkReult = checkAndSign(params);

        if (checkReult.isSuccess()) {
            filterChain.doFilter(myRequestWrapper == null ? request : myRequestWrapper, response);
        } else {
            try {
                mappingJackson2JsonView.render(checkReult.getErrData(), request, response);
            } catch (Exception e) {
                LOGGER.error(e.getMessage(), e);
            }
        }
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值