1、定义拦截器
将来所有的日志记录代码就是写在这个里面,包含请求和响应数据的获取
package com.hzzfxx.intercept;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author betieforever
* @description 描述
* @date 2022/8/9
*/
public class OperationLogHandlerIntercept implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
2.重新定义DispatcherServlet
之所以重新定义改造,是因为HttpServletRequest 和 HttpServletResponse 这两个类的输出流只能被读取一次,再次读写将会读取不到任何内容,所以采用包装类进行改造,方便取请求的数据和响应的数据。
package com.hzzfxx.util.wrapper;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author betieforever
* @description 自定义改造的servlet
* @date 2022/8/7
*/
public class TieDispatcherServlet extends DispatcherServlet {
@Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
super.doDispatch(new ContentCachingRequestWrapper(request), new ContentCachingResponseWrapper(response));
}
}
3.加载拦截器和新的DispatcherServlet核心控制器
@Configuration
public class HandlerInterceptConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogHandlerIntercept());
WebMvcConfigurer.super.addInterceptors(registry);
}
@Bean
@Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
public DispatcherServlet dispatcherServlet(){
return new TieDispatcherServlet();
}
}
4.定义注解类Annotation
用来标识哪些方法、操作需要被拦截(一般情况下,主要记录一些挂件业务的操作日志)以及简单介绍将要拦截的方法的内容是什么等等
import java.lang.annotation.*;
/**
* @author betieforever
* @description 描述
* @date 2022/8/7
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogOperationAnotation {
String value() default "暂无配置";
}
5.配置controller中的方法,标识将要拦截的方法,下面是demo的信息
@RestController
public class YjxxController {
@Autowired
private JdbcTemplate jdbcTemplate;
@RequestMapping("/getDataYjxx")
@LogOperationAnotation("预警信息")
public String getDataYjxx(PageDp pageDp){
.......
}
}
6.获取请求request中参数
这个里面数据非常丰富,可以从header中取相关信息,可以根据不同的contentType调用不同的方法来获取数据(此处待补充.....)application/x- www-form-urlencoded 对应formData,可以使用request.getParameter进行获取;当请求体内容是其它类型时,比如 multipart/form-data或application/json时对应payLoad,无法通过request.getParameter()获取到请求内容,此时只能通过request.getInputStream()和request.getReader()方法获取请求内容
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod)handler;
LogOperationAnotation logOperationAnotation = handlerMethod.getMethodAnnotation(LogOperationAnotation.class);
ContentCachingResponseWrapper contentResponse = (ContentCachingResponseWrapper)(response);
if(logOperationAnotation != null){
String queryString = request.getQueryString();
String formData = request.getParameterMap().entrySet().toString();
String xmlJson = JSON.toJSONString(request.getParameterMap());
String contentType = request.getContentType();
String body = new String(StreamUtils.copyToByteArray(request.getInputStream()));
System.out.println("contentType===" + contentType);
System.out.println("queryString===" + queryString);
System.out.println("formData===" + formData);
System.out.println("xmlJson ===" + xmlJson );
System.out.println("body====" + body);
String operationName = logOperationAnotation.value();
System.out.println("operationName===" + operationName);
}
return true;
}
7.取响应中参数
注意ContentCachingResponseWrapper 在取完数据之后需要调用copyBodyToResponse()方法,只有这样响应里面才会有数据返回,否则响应里面 是没有数据的
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod)handler;
LogOperationAnotation logOperationAnotation = handlerMethod.getMethodAnnotation(LogOperationAnotation.class);
ContentCachingResponseWrapper contentResponse = (ContentCachingResponseWrapper)(response);
if(logOperationAnotation != null){
String responseBody= new String(contentResponse.getContentAsByteArray(),contentResponse.getCharacterEncoding());
System.out.println("responseBody===" + responseBody.substring(0,100));
System.out.println("responseContentType===" + response.getContentType());
}
contentResponse.copyBodyToResponse();
}