Bootstrap

SpringBoot读取@RequestBody参数

前言

        之前在开发中遇到了切面中获取@RequestBody参数问题,查阅网上资料后写了下面这篇文章 SpringBoot AOP获取@RequestBody参数

        但是在实际环境下如果在别的地方调用比如HandlerInterceptor拦截器中调用获取接口参数的方法会报错getReader() has already been called for this request

        下面对方法进行优化,因为@RequestBody 是流的形式进行读取,流读了一次就没有了,所以可以写一个过滤器,在过滤器里面 把流数据 copy一份出来用,别的地方调用的时候使用我们复写的流数据就行。


创建过滤器

文件导入的方法可以过滤掉。

import cn.hutool.core.util.URLUtil;
import org.springframework.http.MediaType;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class BodyWrapperFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        String contentType = servletRequest.getContentType();
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        String requestUri = URLUtil.getPath(req.getRequestURI());
        if(servletRequest instanceof HttpServletRequest
                && !MediaType.MULTIPART_FORM_DATA_VALUE.equals(contentType)
                && !"/api/v1/dsm/monitor/importPower".equals(requestUri)) {
            requestWrapper = new CustomHttpServletRequestWrapper((HttpServletRequest) servletRequest);
        }
        if(requestWrapper == null) {
            // 过滤器包装request不需要,将返回原来的request
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            // 过滤器包装request成功
            filterChain.doFilter(requestWrapper, servletResponse);
        }
    }
}

重写RequestWrapper

在方法中拿出body给自己用

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * 重写一个自己的 RequestWrapper 拿出body给自己用
 *
 */
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;

    public CustomHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        BufferedReader reader = request.getReader();
        try (StringWriter writer = new StringWriter()) {
            int read;
            char[] buf = new char[1024 * 8];
            while ((read = reader.read(buf)) != -1) {
                writer.write(buf, 0, read);
            }
            this.body = writer.getBuffer().toString().getBytes();
        }
    }

    public String getBody(){
        return new String(body, StandardCharsets.UTF_8);
    }

    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }

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

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

自定义bean

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WebApplicationConfig {

    @Bean
    BodyWrapperFilter getBodyWrapperFilter(){
        return new BodyWrapperFilter();
    }
    
    @Bean("bodyWrapperFilter")
    public FilterRegistrationBean<BodyWrapperFilter> checkUserFilter(BodyWrapperFilter bodyWrapperFilter) {
        FilterRegistrationBean<BodyWrapperFilter> registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(bodyWrapperFilter);
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(1);
        registrationBean.setAsyncSupported(true);
        return registrationBean;
    }
}

方法使用

CustomHttpServletRequestWrapper wrapper = (CustomHttpServletRequestWrapper) request;
String param = wrapper.getBody();

参考资料:getReader() has already been called for this request

;