前言
之前在开发中遇到了切面中获取@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();