在spring中,filter都默认继承OncePerRequestFilter,但为什么要这样呢?
OncePerRequestFilter顾名思义,他能够确保在一次请求只通过一次filter,而不需要重复执行。
- AccessLogFilter 核心代码
public class AccessLogFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(AccessLogFilter .class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
Long startTime = System.currentTimeMillis();
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
filterChain.doFilter(requestWrapper, responseWrapper);
String requestPayload = new String(requestWrapper.getContentAsByteArray(),
request.getCharacterEncoding());
String responsePayload = IOUtils.toString(responseWrapper.getContentInputStream(), responseWrapper.getCharacterEncoding());
responseWrapper.copyBodyToResponse();
}
}
其核心思想是实现了OncePerRequestFilter这个虚基类,而这个虚基类是实现了Filter接口,并且要基于SpringMVC框架的,因此这个方法只能适用于SpringMVC工程
核心方法是doFilterInternal
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
Long startTime = System.currentTimeMillis();
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
filterChain.doFilter(requestWrapper, responseWrapper);
String requestPayload = getPayLoad(requestWrapper.getContentAsByteArray(),
request.getCharacterEncoding());
String responsePayload = getPayLoad(responseWrapper.getContentAsByteArray(),
response.getCharacterEncoding());
responseWrapper.copyBodyToResponse();
StringBuilder logResult = generateResultLogger(logMap);
if (LOGGER.isInfoEnabled()){
LOGGER.info(logResult.toString());
}
}
首先通过
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
这两个方式对请求和返回进行包装(让body可以被缓存),这样来解决inputstream不能多次读取的问题
调用
filterChain.doFilter(requestWrapper, responseWrapper);
来执行其他的filter之后
可以通过
String requestPayload = new String(requestWrapper.getContentAsByteArray(),
request.getCharacterEncoding());
String responsePayload = IOUtils.toString(responseWrapper.getContentInputStream(), responseWrapper.getCharacterEncoding());
这种方式取出request和response的内容,最后一定不要忘记
responseWrapper.copyBodyToResponse();
重新写入response
最后打印出整个log