关键词:
请求参数,响应参数,过滤器,拦截器,日志过滤器,配置过滤器
一、目的:
对系统请求参数和响应参数,进行输出打印。支持表单提交及json提交。
二、过滤器与拦截器
首先,了解一下过滤器和拦截器。两者功能很类似,但具体技术实现,相去甚远。对比两者的区别前,先理解一下AOP,AOP不是一种具体的技术,而是一种编程思想。在面向对象编程的过程中,我们很容易通过继承、多态来解决纵向扩展。 但是对于横向的功能,比如,在所有的service方法中开启事务,或者统一记录日志等功能,面向对象的是无法解决的。所以AOP——面向切面编程其实是面向对象编程思想的一个补充。而我们今天讲的过滤器和拦截器都属于面向切面编程的具体实现。而两者的主要区别包括以下几个方面:
1、Filter是依赖于Servlet容器,属于Servlet规范的一部分;而拦截器则是独立存在的,可以在任何情况下使用。
2、Filter的执行由Servlet容器回调完成;而拦截器通常通过动态代理的方式来执行。
3、Filter的生命周期由Servlet容器管理;而拦截器则可以通过IoC容器来管理;因此,可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。
三、日志过滤器_代码
import com.alibaba.fastjson.JSON;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* 如果包含header参数,不做输出
* 顺便 输出对应url 请求体,响应体,耗时
*/
public class LogFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(LogFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
long requestTime = System.currentTimeMillis();
String uri = request.getRequestURI();
String contextPath = request.getContextPath();
String url = uri.substring(contextPath.length());
//静态资源 跳过
if (url.contains(".")) {
filterChain.doFilter(request, response);
return;
}
// 输出请求体
String requestBody = "";
String requestContentType = request.getHeader(HttpHeaders.CONTENT_TYPE);
if (requestContentType != null){
// xml json
if (requestContentType.startsWith(MediaType.APPLICATION_JSON_VALUE) || requestContentType.startsWith(MediaType.APPLICATION_XML_VALUE)){
requestBody = getRequestBody(request);
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8));
request = new HttpServletRequestWrapper(request) {
@Override
public ServletInputStream getInputStream() throws IOException {
return new ByteArrayServletInputStream(byteArrayInputStream);
}
};
// 普通表单提交
}else if (requestContentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED_VALUE)){
requestBody = toJson(request.getParameterMap());
// 文件表单提交
}else if (requestContentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)){
requestBody = getFormParam(request);
}
}
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
response = new HttpServletResponseWrapper(response) {
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new TeeServletOutputStream(super.getOutputStream(), byteArrayOutputStream);
}
};
filterChain.doFilter(request, response);
long costTime = System.currentTimeMillis() - requestTime;
String responseBody = "";
// 暂定只有json 输出响应体
String contentType = response.getHeader(HttpHeaders.CONTENT_TYPE);
if (contentType != null && contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)){
responseBody = byteArrayOutputStream.toString();
}
if (response.getStatus() >= 200 && response.getStatus() < 300) {
log.info("URL:{}, total time:{} ms, responseCode:{}, requestBody:{}, responseBody:{}", url, costTime, response.getStatus(), requestBody, responseBody);
}else {
log.error("URL:{}, total time:{} ms, responseCode:{}, requestBody:{}, responseBody:{}", url, costTime, response.getStatus(), requestBody, responseBody);
}
}
private String getRequestBody(HttpServletRequest request) {
int contentLength = request.getContentLength();
if(contentLength <= 0){
return "";
}
try {
return IOUtils.toString(request.getReader());
} catch (IOException e) {
log.error("获取请求体失败", e);
return "";
}
}
private String getFormParam(HttpServletRequest request) {
MultipartResolver resolver = new StandardServletMultipartResolver();
MultipartHttpServletRequest mRequest = resolver.resolveMultipart(request);
Map<String,Object> param = new HashMap<>();
Map<String,String[]> parameterMap = mRequest.getParameterMap();
if (!parameterMap.isEmpty()){
param.putAll(parameterMap);
}
Map<String, MultipartFile> fileMap = mRequest.getFileMap();
if(!fileMap.isEmpty()){
for (Map.Entry<String, MultipartFile> fileEntry : fileMap.entrySet()) {
MultipartFile file = fileEntry.getValue();
param.put(fileEntry.getKey(), file.getOriginalFilename()+ "(" + file.getSize()+" byte)");
}
}
return toJson(param);
}
private static String toJson(Object object){
return JSON.toJSONStringWithDateFormat(object, "yyyy-MM-dd HH:mm:ss");
}
}
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
* Created by liuxd on 2020/06/20.
*/
public class ByteArrayServletInputStream extends ServletInputStream {
private ByteArrayInputStream byteArrayInputStream;
public ByteArrayServletInputStream(ByteArrayInputStream byteArrayInputStream) {
this.byteArrayInputStream = byteArrayInputStream;
}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
}
import org.apache.commons.io.output.TeeOutputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import java.io.IOException;
import java.io.OutputStream;
/**
* Created by liuxd on 2020/06/20.
*/
public class TeeServletOutputStream extends ServletOutputStream {
private final TeeOutputStream teeOutputStream;
public TeeServletOutputStream(OutputStream one, OutputStream two) {
this.teeOutputStream = new TeeOutputStream(one, two);
}
@Override
public void write(byte[] b) throws IOException {
this.teeOutputStream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
this.teeOutputStream.write(b, off, len);
}
@Override
public void write(int b) throws IOException {
this.teeOutputStream.write(b);
}
@Override
public void flush() throws IOException {
super.flush();
this.teeOutputStream.flush();
}
@Override
public void close() throws IOException {
super.close();
this.teeOutputStream.close();
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
}
请留意代码:filterChain.doFilter(request, response);
即在方法执行前先记录时间戳,以及请求参数;然后,通过过滤器链完成请求的执行;在此次之后,收集响应参数,在返回结果之间计算执行打印。
四、配置过滤器_代码
import com.myfutech.common.spring.filter.LogFilter;
import com.myfutech.common.spring.filter.UserInfoFilter;
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 logFilter() {
final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
final LogFilter logFilter = new LogFilter();
filterRegistrationBean.setFilter(logFilter);
return filterRegistrationBean;
}
}
参考:
https://www.cnblogs.com/paddix/p/8365558.html