request的参数会使用流的方式获取,这就意味着读取一次之后会关闭,如果使用过滤器/拦截器进行参数处理之后,controller层就无法获取参数了,可能会报的错误 如:stream closed

我也是照着网上的大部分方案,把流拷贝出来,这样确实解决了我使用json传递的问题,但是我是用的方法不能解决formdata的传递;其实这个wrapper框架已经提供给了我们: org.springframework.web.util.ContentCachingRequestWrapper

ContentCachingRequestWrapper

我们不需要去封装方法处理请求流,使用spring自带的requestWrapper,这里有个问题:spring缓存的请求参数只有在调用流之后才会有效,这时候在filter或者拦截器之前访问还是获取不到请求参数,这样做的好处就是节约内存,如果我们自己的应用可以采用第二种方法;

/**
 * 配合@ServletComponentScan扫描自定义的过滤器即可
 * @author JGMa
 */
@WebFilter(urlPatterns = "/*",filterName = "channelFilter")
public class RequestWrapperFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		// 主要是这里
        chain.doFilter(new ContentCachingRequestWrapper((HttpServletRequest) request), response);
    }
}

拦截器中使用

ContentCachingRequestWrapper requestWrapper = null;
// 注意不要二次包装
        if (request instanceof ContentCachingRequestWrapper){
            requestWrapper = (ContentCachingRequestWrapper)request;
        }else {
            requestWrapper = new ContentCachingRequestWrapper(request);
        }
// 读取请求参数
 String jsonPostParam = StringUtils.toEncodedString(requestWrapper.getContentAsByteArray(), Charset.forName(requestWrapper.getCharacterEncoding()));

方法二:自定义缓冲流


import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;

/**
 * @author JGMa
 */
public class RepeatableInputStream extends ServletInputStream {
    // 底层是个字节数组
    final ByteArrayInputStream byteArrayInputStream;

    public RepeatableInputStream(byte[] requestBodyBytes) {
        // 实例化的时候就把读取到的字节缓存起来
        this.byteArrayInputStream =
                new ByteArrayInputStream(requestBodyBytes);
    }

    /* 下面的都是ServletInputStream的必要重写方法 */
    @Override
    public boolean isFinished() {
        return byteArrayInputStream.available() == 0;
    }

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

    @Override
    public void setReadListener(ReadListener readListener) {
        // Not implemented
    }

    @Override
    public int read() throws IOException {
        // 直接从缓存读取
        return byteArrayInputStream.read();
    }
}


import org.springframework.util.StreamUtils;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * @author JGMa
 */
public class RepeatableRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;
    private Charset charset;

    public RepeatableRequestWrapper(HttpServletRequest request, Charset charset) throws IOException {
        super(request);
        body = StreamUtils.copyToByteArray(request.getInputStream());
        this.charset = charset;
    }

    public RepeatableRequestWrapper(HttpServletRequest request) throws IOException {
        this(request, StandardCharsets.UTF_8);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new RepeatableInputStream(body);
    }

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

    /* 两个读取body的辅助方法 */
    public byte[] getRequestBodyBytes() {
        return body;
    }

    public String getRequestBodyString() {
        return new String(body,charset);
    }

}

使用

RepeatableRequestWrapper requestWrapper = null;

        if (request instanceof RepeatableRequestWrapper){
            requestWrapper = (RepeatableRequestWrapper)request;
        }else {
            requestWrapper = new RepeatableRequestWrapper(request);
        }
            // 获取请求中的JSON数据
            String json = requestWrapper.getRequestBodyString();
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐