三种实现日志过滤器的方式 (过滤器 (Filter)、拦截器(Interceptors)和切面(Aspect))
1.建立RequestWrapper类
import com.g2.order.server.utils.HttpHelper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.Enumeration; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper { private final byte[] body; public HttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); System.out.println("-------------------------------------------------"); Enumeration e = request.getHeaderNames() ; while(e.hasMoreElements()){ String name = (String) e.nextElement(); String value = request.getHeader(name); System.out.println(name+" = "+value); } body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8")); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public boolean isFinished() { return true; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener listener) { } @Override public int read() throws IOException { return bais.read(); } }; } @Override public String getHeader(String name) { return super.getHeader(name); } @Override public Enumeration<String> getHeaderNames() { return super.getHeaderNames(); } @Override public Enumeration<String> getHeaders(String name) { return super.getHeaders(name); } public String getStringBody(){ return new String(body); } }
2.定义ResponseWrapper
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; import javax.servlet.http.HttpServletResponse; public class HttpServletResponseWrapper extends javax.servlet.http.HttpServletResponseWrapper { private ByteArrayOutputStream buffer = null; private ServletOutputStream out = null; private PrintWriter writer = null; public HttpServletResponseWrapper(HttpServletResponse response) throws IOException{ super(response); buffer = new ByteArrayOutputStream(); out = new WapperedOutputStream(buffer); writer = new PrintWriter(new OutputStreamWriter(buffer, "UTF-8")); } //重载父类获取outputstream的方法 @Override public ServletOutputStream getOutputStream() throws IOException { return out; } @Override public PrintWriter getWriter() throws IOException { return writer; } @Override public void flushBuffer() throws IOException { if (out != null) { out.flush(); } if (writer != null) { writer.flush(); } } @Override public void reset() { buffer.reset(); } public String getResponseData(String charset) throws IOException { flushBuffer();//将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据 byte[] bytes = buffer.toByteArray(); try { return new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException e) { return ""; } } //内部类,对ServletOutputStream进行包装,指定输出流的输出端 private class WapperedOutputStream extends ServletOutputStream { private ByteArrayOutputStream bos = null; public WapperedOutputStream(ByteArrayOutputStream stream) throws IOException { bos = stream; } //将指定字节写入输出流bos @Override public void write(int b) throws IOException { bos.write(b); } @Override public boolean isReady() { return false; } @Override public void setWriteListener(WriteListener listener) { } } }
这里有三种实现方式 (过滤器 (Filter)、拦截器(Interceptors)和切面(Aspect))
3.1 建立过滤器
import com.g2.order.server.api.HttpServletRequestWrapper; import com.g2.order.server.api.HttpServletResponseWrapper; import com.g2.order.server.utils.HttpHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebFilter(filterName = "accessLog", urlPatterns = "/*") public class AccessLogFilter implements Filter { private static Logger logger = LoggerFactory.getLogger(AccessLogFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequestWrapper requestWrapper; if (request instanceof HttpServletRequestWrapper) { requestWrapper = (HttpServletRequestWrapper) request; } else { requestWrapper = new HttpServletRequestWrapper((HttpServletRequest) request); } HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper((HttpServletResponse) response); chain.doFilter(requestWrapper, responseWrapper); String result = responseWrapper.getResponseData(response.getCharacterEncoding()); response.getOutputStream().write(result.getBytes()); logger.info("请求值链接:{},method:{},body:{},header:{},response:{}" , requestWrapper.getRequestURI() , requestWrapper.getMethod() , requestWrapper.getStringBody() , HttpHelper.getStringHeaders(requestWrapper) , result); } @Override public void destroy() { } }
4.启动类
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.web.servlet.ServletComponentScan; /** * 程序入口 */ @SpringBootApplication @ServletComponentScan public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
5.建立controller
import com.g2.order.dao.mapper.user.UserMapper; import com.g2.order.server.vo.user.UserLoginReq; import com.g2.order.dao.model.user.UserDao; import com.g2.order.server.vo.user.UserLoginResp; import com.g2.order.server.vo.user.UserModel; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; @Api(value = "HomeController", description = "用户登录登出接口") @RestController @RequestMapping("/home") public class HomeController { @Autowired private UserMapper userMapper; @ApiOperation(value = "用户登录", notes = "用户登录接口") @RequestMapping(value = "/login", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public UserLoginResp login(@RequestBody UserLoginReq req) { UserDao userDao = userMapper.getById(1); UserModel userModel = new UserModel(); userModel.setUserId(Integer.toString(userDao.getUserId())); return new UserLoginResp(userModel); } }
6.请求后的日志
请求值链接:/home/login,method:POST,body:{ "userId":"123","password":"123444"},header:cache-control:no-cache
postman-token:a75b1ed5-27d4-47ea-ba21-ee368b463fc5
content-type:application/json
user-agent:PostmanRuntime/2.3.2
host:127.0.0.1:88
accept-encoding:gzip, deflate
content-length:42
connection:keep-alive
返回值:{"success":true,"errorMessage":"","payload":{"userId":"1","roleName":null,"roleId":null}}
如果使用 拦截器(Interceptors)代码会更加简单
import com.google.common.base.Strings; import com.g2.order.server.api.HttpServletRequestWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.Arrays; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import static java.util.stream.Collectors.joining; public class AccessLogInterceptor extends HandlerInterceptorAdapter { private static Logger logger = LoggerFactory.getLogger(AccessLogInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String body = ""; logger.debug("Access Auth Interceptor - 进入拦截器"); if (request instanceof HttpServletRequestWrapper) { HttpServletRequestWrapper requestWrapper = (HttpServletRequestWrapper) request; body = requestWrapper.getStringBody(); } if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); Class[] paramTypes = method.getParameterTypes(); String paramTypesString = Arrays.stream(paramTypes).map(p -> p.getName()).collect(joining(",")); Parameter[] methodParameters = method.getParameters(); if (methodParameters.length == 0) { return false; } Parameter methodParameter = methodParameters[0]; String parameterName = methodParameter.getName(); String parameterClass = methodParameter.getType().getCanonicalName(); String parameterVelue = request.getParameter(parameterName); if (Strings.isNullOrEmpty(parameterVelue)) { parameterVelue = body; } logger.info("请求方法:{},请求参数类型:{},请求值:{}", method.getName(), parameterClass, parameterVelue); } return true; } /** * This implementation is empty. */ @Override public void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } /** * This implementation is empty. */ @Override public void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } /** * This implementation is empty. */ @Override public void afterConcurrentHandlingStarted( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { } }
再建立Config 类
import com.g2.order.server.interceptor.AccessLogInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.servlet.config.annotation.InterceptorRegistration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Component public class LogInterceptorConfig extends WebMvcConfigurerAdapter { @Bean public AccessLogInterceptor getAccessLogInterceptor() { return new AccessLogInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { InterceptorRegistration addInterceptor = registry.addInterceptor(getAccessLogInterceptor()); // 排除配置 addInterceptor.excludePathPatterns("/error"); // 拦截配置 addInterceptor.addPathPatterns("/**"); } }
3.3 对controller层使用切面(Aspect)(代码最简单,功能最强大)
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import javax.annotation.Resource;
//开启AspectJ 自动代理模式,如果不填proxyTargetClass=true,默认为false,
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Component
@Aspect
public class ControllerAspectConfig {
@Around("execution(* com.g2.order.server.controller.*.*(..))")
public Object handleControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("进入切面,执行before..");
//获取controller对应的方法.
org.aspectj.lang.reflect.MethodSignature methodSignature = (org.aspectj.lang.reflect.MethodSignature) proceedingJoinPoint.getSignature();
//获取方法所在的类(controller)
Class beanType = methodSignature.getDeclaringType();
//获取方法
Method method = methodSignature.getMethod();
//获取方法参数列表(无需处理讨厌的流了)
Object[] args = proceedingJoinPoint.getArgs();
for (Object arg : args) {
//获取参数的类型与值
System.out.println(arg.getClass().getName());
System.out.println("arg is " + arg);
}
long startTime = System.currentTimeMillis();
System.out.println("进入其他切面或业务执行..");
Object obj = proceedingJoinPoint.proceed();
System.out.println("业务完成,执行after..");
//获取返回值的类型,与 Method.getReturnType()一致
Class responseClass=obj.getClass();
System.out.println("time aspect 耗时" + (System.currentTimeMillis() - startTime));
//方法的返回值是:
System.out.println("response is " + obj);
return obj;
}
}
拦截器和过滤器的区别
spring boot RESTFul API拦截 以及Filter和interceptor 、Aspect区别
拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
过滤器(Filter) :可以拿到原始的http请求,但是拿不到你请求的控制器和请求控制器中的方法的信息。
拦截器(Interceptor):可以拿到你请求的控制器和方法,却拿不到请求方法的参数。
切片 (Aspect) : 可以拿到方法的参数,但是却拿不到http请求和响应的对象