问题描述
在工作中,我们需要通过过滤器对请求进行拦截,并记录请求的日志,在拦截请求时,我们可能会需要读取请求的json数据,但是通过request.getInputStream() 与 request.getReader() 只能调用一次,因为是流,读取完就没了。 然而,如在前端过滤器中,拦截请求数据并记录日志,记录日志后,请求继续向后传递,在后面的业务代码中,仍然需要继续取得请求的数据。
进过一顿搜索终于找到了解决方法,那就是重写HttpServletRequestWrapper,自己实现一个Wrapper。
解决方法
java
import lombok.Getter;
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.InputStreamReader;
@Getter
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder sb = new StringBuilder();
String line;
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null) {
sb.append(line);
}
body = sb.toString();
//将post的body数据放入缓存,这样子在后面就能够随时取用
request.setAttribute("body", body);
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
在最前端的filter中,new一个自己的wrapper,并doChain继续向后传递即可。
java
import cn.hutool.core.util.StrUtil;
import com.kelebb.utilities.requests.RequestWrapper;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 此过滤器用途
* 因为Request的请求方式为JSON时,参数流只能读取一次
* 可能导致在过滤器、拦截器、AOP做处理后无法正常获取参数
* 需在此做处理
*/
@Component
@Order(1)
public class MultiReadHttpServletRequestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest httpServletRequest) {
String contentType = httpServletRequest.getHeader("Content-Type");
// 只对json参数做处理
if (StrUtil.isNotEmpty(contentType) && contentType.contains("application/json")) {
RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
chain.doFilter(requestWrapper, response);
}else {
chain.doFilter(request, response);
}
} else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
Filter.super.destroy();
}
}