调用 request.getParameter 之后再调用 request.getInputStream 取不到流数据

本文描述了一个在Spring拦截器中遇到的问题,即在调用`request.getParameter`后无法从`request.getInputStream`获取POST请求的流数据。经过排查,发现是由于`getParameter`方法影响了流数据的获取。解决方案是在拦截器中根据请求类型分别处理,对于POST请求,先读取流并回写到request,确保后续控制器可以正常处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0x00 问题描述 & 背景

随着项目的推进,增加了权限控制模块,具体控制到接口层(controller)。每次调用接口时需要校验用户是否有权限。用的方案是spring的拦截器。

前端(APP)所有的请求都会携带参数sessionId,作为用户的标识。其中Get请求参数是通过url传递的,post请求参数是通过二进制流传递的。

拦截器在获取sessionId的时候先执行request.getParameter("sessionId"),如果没有拿到sessionId,则调用request.getInputStream()获取流信息。具体代码如下:

private String getSessionId(HttpServletRequest request) {
    String sessionId = request.getParameter("sessionId");
    if (sessionId == null) {
        // 参数也可能为sessionid
        sessionId = request.getParameter("sessionid");
    }
    if (sessionId == null) {
        // post请求
        String data = getRequestStringData(request);
        System.out.println("data = " + data);
        if (data.length() > 0) {
            // TODO get SeesionId from json
        }
    }
    return sessionId;
}

private String getRequestStringData(HttpServletRequest request) {
    StringBuffer info = new java.lang.StringBuffer();
    InputStream in = null;
    try {
        in = request.getInputStream();
        BufferedInputStream buf = new BufferedInputStream(in);
        byte[] buffer = new byte[1024];
        int iRead;
        while ((iRead = buf.read(buffer)) != -1) {
            info.append(new String(buffer, 0, iRead, "utf-8"));
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (null != in) {
                in.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return info.toString();
}

测试发现post请求始终拿不到sessionId,加点日志发现整个流信息都为空。

0x01 问题排查过程

  1. 用postman模拟App请求,发现能拿到流信息,初步怀疑是App的同事改了post请求发送方式;
  2. App的同事(Android和iOS)都说没有动过,并且断点调试,确实正确发送了请求;
  3. java端注掉拦截器,post请求直接达到controller,controller自然是直接获取流信息的,没有问题,能够正确拿到流信息;
  4. 查看api文档,api文档中有这样一句:

    If the parameter data was sent in the request body, such as occurs with an HTTP POST request, then reading the body directly via getInputStream() or getReader() can interfere with the execution of this method.
    
  5. 问题产生的原因已经明了,getParameter方法会影响getInputStream方法获取流信息。

0x02 解决方案

在获取sessionId的时候先判断是Post请求还是Get请求,如果是Get请求就调用getParameter方法,Post请求则调用getInputStream方法。

还有一点需要注意,流数据只能读一次,在拦截器中获取完流数据之后需要回写到request,以便controller使用。代码如下:

private String getSessionId(HttpServletRequest request) {
    String sessionId = "";
    logger.info("请求方式:"+request.getMethod());
    if ("GET".equalsIgnoreCase(request.getMethod())) {
        sessionId = request.getParameter("sessionId");
        if (sessionId == null) {
            // 参数也可能为sessionid
            sessionId = request.getParameter("sessionid");
        }
    } else if ("POST".equalsIgnoreCase(request.getMethod())) {
        // post请求
        String data = getRequestStringData(request);
        request.setAttribute("postData", data); // 流数据回写
        if (data.length() > 0) {
            JSONObject jsonObject = JSONObject.fromObject(data);
            sessionId = jsonObject.getString("sessionId");
        }
    }
    return sessionId;
}

0x03 后记 & 疑问

  1. 如果哪位看官有更深的理解,欢迎指教。
  2. App端都是调用自带SDK发送的post请求,在问题修复之前,为什么App发送的请求息拿不到流信息而postman发送的则能拿到?烦请有研究的同学告知。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值