Java过滤器
通过实现Filter接口,重写doFilter方法做增强操作。 Filter 并不是一个标准的Servlet ,它不能处理用户请求,也不能对客户端生成响应。 主要用于对HttpServletRequest 进行预处理,也可以对HttpServletResponse 进行后处理,是个典型的处理链 Filter有三个方法init、doFilter、destroy,对应请求初始化时、执行时和销毁时,主要在doFilter里面做文章 doFilter方法三个参数,请求体,响应体,过滤器链 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
案例:给所有请求增加日志信息打印(入参、出参)
@Component
//自定义一个过滤器实现Filter接口、配置@WebFilter注解,配置拦截路径
@WebFilter(filterName = "myFilter", urlPatterns = {"/*"})
@Slf4j
public class RequestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@SneakyThrows
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String requestId = httpServletRequest.getHeader("requestId");
if(StringUtil.isBlank(requestId)){
requestId = UUID.randomUUID().toString().replaceAll("-", "");
}
MDC.put("requestId", requestId);
//这里只针对application/json 进行日志打印
if (httpServletRequest.getContentType() != null && httpServletRequest.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {
//转换成代理类(代理类作用是为了拿到请求头和响应体的内容)
ResponseWrapper wrapperResponse = new ResponseWrapper(httpServletResponse);
RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
long before = System.currentTimeMillis();
// 打印请求 url,ip
log.info("请求IP:{}, URL:{},", httpServletRequest.getRemoteAddr(), httpServletRequest.getRequestURL().toString());
log.info("请求参数:{}", JSON.toJSONString(httpServletRequest.getParameterMap()));
// 打印请求入参
log.info("请求体:{} ", JSON.toJSONString(requestWrapper.getBody()));
filterChain.doFilter(requestWrapper, wrapperResponse);
//获取返回值
byte[] content = wrapperResponse.getContent();
// 打印出参
log.info("返回结果: " + JSON.toJSON(new String(content)));
// 执行耗时
log.info("耗时: " + (System.currentTimeMillis() - before) + " ms");
MDC.remove("requestId");
//把返回值输出到客户端
ServletOutputStream out = httpServletResponse.getOutputStream();
out.write(content);
out.flush();
} else {
filterChain.doFilter(servletRequest, servletResponse);
MDC.remove("requestId");
}
}
}
FilterChain 接口,该接口由容器实现,其实它的作用是一个回调接口,它只有一个方法doFilter。可以定义很多个Filter,都会执行
代码里有个东西叫MDC
MDC是什么?能干什么?
是什么? MDC 全称是 Mapped Diagnostic Context,可以粗略的理解成是一个线程安全的存放诊断日志的容器。 能干什么? 在Web应用中,如果想在日志中输出请求用户IP地址、请求URL、统计耗时等,MDC基本都能支撑; 在Web应用中,如果能画出用户的请求到响应整个过程,势必会快速定位生产问题,那么借助 MDC 来保存用户请求时产生的 requestId,当请求完成后,再将这个 requestId 进行移除,这么一来通过 grep requestId 就能轻松查出整个请求流程的日志轨迹。
另,请求和响应的代理类,封装类
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
InputStream inputStream = null;
try {
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) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
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 class ResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream buffer;
private ServletOutputStream out;
public ResponseWrapper(HttpServletResponse httpServletResponse) {
super(httpServletResponse);
buffer = new ByteArrayOutputStream();
out = new WrapperOutputStream(buffer);
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return out;
}
@Override
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
}
public byte[] getContent() throws IOException {
flushBuffer();
return buffer.toByteArray();
}
class WrapperOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos;
public WrapperOutputStream(ByteArrayOutputStream bos) {
this.bos = bos;
}
@Override
public void write(int b) throws IOException {
bos.write(b);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener arg0) {
}
}
}