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請求和響應的對象