解决getInputStream()和getReader()只能调用一次问题

最近在项目开发中,需要记录请求日志,刚开始ContentType的类型都是application/x-www-form-urlencoded这种,所以我们在获取请求记录时,可以通过如下方式获取

Enumeration<?> temp = request.getParameterNames();
			        String params_json = "";
			        if (null != temp) {
			            while (temp.hasMoreElements()) {
			                String en = (String) temp.nextElement();
			                String value = request.getParameter(en);
			                res.put(en, value);
			                //如果字段的值为空,判断若值为空,则删除这个字段>
			                if (null == res.get(en) || "".equals(res.get(en))) {
			                    res.remove(en);
			                }
			            }
						params_json = gson.toJson(res);
			        }

但是当类型为application/json时,发现使用request.getParameterNames()这种方式拿不到数据了。是因为application/json这种我们都是通过@RequestBody来接收的

而 @RequestBody是通过读流方式来获取的(这个是重点),那么我们就需要通过流的方式来获取参数,于是写了如下代码获取

/**
	 * 流转为json字符串
	 * @param req
	 * @return
	 */
	private String getRequestPayload(HttpServletRequest req) {
		StringBuilder sb = new StringBuilder();
		try{
			req.setCharacterEncoding("UTF-8");
			BufferedReader reader=null;
			reader = new BufferedReader(new InputStreamReader(req.getInputStream(),"UTF-8"));
			String line=null;
			while((line = reader.readLine())!=null){
				sb.append(line);
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
		return sb.toString();
	}

但是一调用,直接报错了,提示类似错误:java.lang.IllegalStateException: getReader() has already been called for this request

这个错误就是说:getInputStream()和getReader()只能调用一次,前面我已经说了@RequestBody已经读了一次流了,所以在读就会报错。

那么如何解决:我们需要继承HttpServletRequestWrapper来重写getInputStream和getReader方法

package com.toutiao_efficiency.common.requestWrapper;

import org.apache.commons.lang.StringUtils;
import org.springframework.util.StreamUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author 徐长城
 * @Date 2020/11/17 17:29
 * @Description : 解决getInputStream()和getReader()只能调用一次
 */
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private byte[] requestBody = null;//用于将流保存下来

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        requestBody = StreamUtils.copyToByteArray(request.getInputStream());

    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);

        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException{
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}

写好了以后,我们需要在filter中使用它,如图:

判断当类型为application/json,就行包装转换,这样就可以解决不能多次读流问题了

评论 2 您还未登录,请先 登录 后发表或查看评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:程序猿惹谁了 设计师:我叫白小胖 返回首页

打赏作者

前方太黑暗

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值