使用场景:在接口处理之前,我们需要拿到请求参数,对参数进行校验。注意,这里需要拿到的是原始的请求信息!
一般的获取方式
ServletInputStream inputStream = request.getInputStream();
StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, request.getCharacterEncoding()))) {
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
String requestBody = stringBuilder.toString();
获取requestBody的代码大同小异,可自行搜索。代码没有问题,但是无法获取到requestBody,就要考虑接口方法是否使用了@RequestBody。
在Spring MVC框架中,当你使用@RequestBody
注解时,Spring会在将请求转发到控制器方法之前,从HTTP请求体中读取数据并将其反序列化为Java对象。这一处理过程是由HttpMessageConverter
完成的,它负责将请求体中的JSON、XML或其他格式的数据转换为Java对象。
请求体只能被读取一次:HTTP请求体是一个输入流,它只能被读取一次。一旦HttpMessageConverter读取了请求体,输入流就被“消耗”掉了,无法再次被读取。因此,当切面尝试访问请求体时,它发现输入流已经为空或不可用。
解决方法:
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.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public class CachedBodyHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] cachedBody;
public CachedBodyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
InputStream inputStream = request.getInputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
cachedBody = outputStream.toByteArray();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cachedBody);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return byteArrayInputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
}
public String getCachedBody() {
return new String(cachedBody, StandardCharsets.UTF_8);
}
}
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class CachedBodyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
CachedBodyHttpServletRequestWrapper cachedBodyRequestWrapper = new CachedBodyHttpServletRequestWrapper(httpRequest);
chain.doFilter(cachedBodyRequestWrapper, response);
} else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
}
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<CachedBodyFilter> loggingFilter(){
FilterRegistrationBean<CachedBodyFilter> registrationBean
= new FilterRegistrationBean<>();
registrationBean.setFilter(new CachedBodyFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
然后在切面里利用CachedBodyHttpServletRequestWrapper 去获取requestBody就可以了。