问题产生原因

  1. POST 请求
  2. 使用FilterInterceptorHttpServletRequest中的数据进行了读取
  3. 在 Controller 中使用@RequestBody解析请求参数
  • 如果看官使用的是ShiroFilter,建议替换成 Servlet 原生的 Filter,对于 Shiro 过滤器出现该问题时的解决方案,暂时还没有研究出来

分析

  • HttpServletRequest好比是容器(瓶子),其自带的信息流(InputStream)相当于。进行读取时,会调用getInputStream(),这个操作就是从容器(瓶子)中把水抽取出来,这时候容器中的就没了,当再想抽取时,自然无法再获得

  • 复现一下我们报错的场景,先在Filter中通过HttpServletRequest.getInputStream()读取数据流,这时候容器中的水已经抽取过了,当@RequestBody再去读取时,就会报错:Required request body is missing

  • 更全面的理解可以看这里:https://www.cnblogs.com/yepei/p/7011081.html

解决方案

  • 将数据流可以被多次读取,因此新建一个包装类,继承HttpServletRequest,在该包装类中,获取数据流并进行保存
  1. 定义包装类
package com.w.module.api.filter;

import org.apache.commons.io.IOUtils;

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

/**
 * 包装HttpServletRequest,将数据流进行拷贝
 *
 * @author wujiawei0926@yeah.net
 * @see
 * @since 
 */
public class WrapperHttpServletRequest extends HttpServletRequestWrapper {

    private static byte[] requestBody;


    public WrapperHttpServletRequest(HttpServletRequest request) {
        super(request);
        try {
            requestBody = IOUtils.toByteArray(request.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if(requestBody == null) {
            requestBody = new byte[0];
        }
        return new BufferedServletInputStream(requestBody);
    }

    class BufferedServletInputStream extends ServletInputStream{
        private ByteArrayInputStream inputStream;

        public BufferedServletInputStream(byte[] buffer) {
            this.inputStream = new ByteArrayInputStream( buffer );
        }

        @Override
        public int available() throws IOException {
            return inputStream.available();
        }

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

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            return inputStream.read( b, off, len );
        }

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

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

        @Override
        public void setReadListener(ReadListener readListener) {

        }
    }



}
  1. 定义好包装类后,在自己的Filter或Interceptor中,通过包装类进行inputStream读取

    package com.w.module.api.filter;
    
    import javax.servlet.*;
    
    /**
     *
     * API接口过滤器
     * v1.0
     *
     * @author wujiawei0926@yeah.net
     * @see
     * @since
     */
    @WebFilter
    public class ApiV1Filter implements Filter {
    
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            WrapperHttpServletRequest request = new WrapperHttpServletRequest((HttpServletRequest) servletRequest);
            
    	// TODO 在这里进行数据流的读取或其他操作,所有方法调用方式与HttpServletRequest相同
    
            filterChain.doFilter(request, servletResponse);
        }
    
        @Override
        public void destroy() {
    
        }
    
    }
  2. 这时候重新进行调试,发现不再报错,并且可以获取到参数