为什么要保存请求参数
在springboot中,前端请求发送到后端,其中的数据是通过字节流的形式发送的,默认情况下只能读取一次。然而,在项目中,我们会有多种原因对数据进行解析、校验,其中包括但不局限于以下功能:
- 对敏感词语进行屏蔽
- 前后端的session校验
- 数据的脱敏
因此,我们可能在interceptor中提前获取数据,并在interceptor、controller中使用。在这种情况下,我们需要将前端请求的数据保存下来,方面后续交易过程中的数据使用。
如何保存请求参数
我们通过封装RequestWrapper类,实现将数据放入字段body中,并在filter层调用方法进行设置。
封装RequestWrapper
该功能是一个所有的请求均适用的功能,那么在springboot中,我们可以通过切面,在Filter中完成该功能。 其中,我们需要定制一个新的继承HttpServletRequestWrapper的类,该类可以读取请求的字节流,并保存到改类中的某个字段(比如body)。 其中,该类可以如下定义:
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {
// 保存请求的参数
private String body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
// 保存请求的数据流
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = stringBuilder.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
public void setBody(String body) {
this.body = body;
}
}
修改Filter代码
在定义完RequestWrapper后,我们可以在Filter中配合使用RequestWrapper,从而保存请求的参数。如果Filter未引入项目中,则可以参考springboot--过滤器功能。
在引入Filter后,我们可以在chain.doFilter()函数前面添加代码。具体地,修改后的代码为:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
long now = System.currentTimeMillis();
String requestURI = ((HttpServletRequest) request).getRequestURI();
try {
// 修改代码行 开始
RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest) request);
chain.doFilter(requestWrapper, response);
// 修改代码行 结束
}finally {
// 输出每一笔交易的耗时
log.info("requestUrl: {}, responseTime: {}ms", requestURI, System.currentTimeMillis()-now);
}
}
其中,代码行RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest) request);
会将request的请求参数获取并保存在body中。后续具体的使用数据可以通过request.getBody()获得。
最终我们在封装的request中可以看到请求的参数: