Bootstrap

getReader() has already been called for this request

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

问题原因:HttpServletRequest是只能被读取一次,第二次读取就会报错。
实际开发中,需求场景:

1.代码在不同地方,需要多次读取request中的输入流进行参数校验和修改,第二次读取就会报错。

2.实际开发中,记录请求参数,把请求参数保存到日志中,利用spring aop,在切面内读取post请求参数。spring boot 在封装参数的时候已经读取一次,所以在切面内读取参数已经是第二次读取。

解决方法:

创建一个过滤器,创建一个spring包装过HttpServletRequest,在过滤中把请求参数复制到包装的request中,并通过filter把包装request再继续传递下去,

代码详细如下:

step 1:

创建包装的request, ReadHttpRequestWrapper 类并继承 HttpServletRequestWrapper 包装类

package com.one.order.filter;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

public class ReadHttpRequestWrapper extends HttpServletRequestWrapper {

    private final String body;

    /**
     *
     * @param request
     */
    public ReadHttpRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = request.getReader();
        String readCount = "";
        while((readCount = reader.readLine()) != null){
            sb.append(readCount);
        }
        body = sb.toString();
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream byteArrayIns = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletIns = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

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

            @Override
            public void setReadListener(javax.servlet.ReadListener listener) {

            }

            @Override
            public int read() {
                return byteArrayIns.read();
            }
        };
        return  servletIns;
    }
}

step 2:

创建Filter,往下传递请求;

这里采用了通过继承OncePerRequestFilter方式来实现Filter。

package com.one.order.filter;

import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
@WebFilter("/*")
public class MyFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ReadHttpRequestWrapper requestWrapper = new ReadHttpRequestWrapper(request);
        filterChain.doFilter(requestWrapper, response);
    }
}

 step 3:

测试验证:

也可以在spring  aop切面内或取请求参数,同样不报错。

    /**
     * 测试request.getReader
     */
    @PostMapping("/list")
    public void test(HttpServletRequest request) {
        request.getReader(); // 不报错
        return;
    }

;