可以使用springaop拦截器来记录请求参数日志:
1, pom添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2,新增一个注解
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperLog {
String bizType() default ""; // 业务类型
}
3,新增一个拦截器,拦截上面那个注解标注的方法
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.hdan.happy.annotation.OperLog;
import com.hdan.happy.util.MyRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@Component
@Aspect
@Slf4j
public class OperInterceptor {
// 有这个注解的方法都会被执行
@Pointcut(value = "@annotation(com.hdan.happy.annotation.OperLog)")
public void logPoincut(){
}
/**
* 方法执行完成后,执行保存日志
* @param joinPoint
* @param returnObj 返回参数
*/
@AfterReturning(value="logPoincut()", returning="returnObj")
public void saveOperLog(JoinPoint joinPoint, Object returnObj){
// 获取RequestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 从获取RequestAttributes中获取HttpServletRequest的信息
HttpServletRequest request = (HttpServletRequest) requestAttributes
.resolveReference(RequestAttributes.REFERENCE_REQUEST);
try {
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
String bizType = "";
// 获取操作
OperLog opLog = method.getAnnotation(OperLog.class);
if (opLog != null) {
bizType = opLog.bizType();
}
// 请求的参数
Map<String, Object> paramMap = converMap(request.getParameterMap());
if (isJson(request)) { // 如果是json请求,请求的requestbody参数是在流里面,取不出来。要用下面的方法包装一下,把流里面的参数解析出来
String jsonParam = new MyRequestWrapper(request).getBodyString();
if(StringUtils.isNotBlank(jsonParam)){
paramMap = JSON.parseObject(jsonParam, Map.class);
}
}
// 获取记录操作日志信息
String params = JSON.toJSONString(paramMap);
log.info("请求方法:{}, " +
"请求业务类型:{}, " +
"请求参数:{}",
method, bizType, params);
} catch (Exception e) {
log.error("记录操作日志报错", e);
}
}
/**
* 转换request 请求参数
*
* @param paramMap request获取的参数数组
*/
public Map<String, Object> converMap(Map<String, String[]> paramMap) {
Map<String, Object> rtnMap = new HashMap<>();
for (String key : paramMap.keySet()) {
String[] values = paramMap.get(key);
if(values != null && values.length == 1){
rtnMap.put(key, values[0]);
}else{
rtnMap.put(key, values);
}
}
return rtnMap;
}
private boolean isJson(HttpServletRequest request) {
if (request.getContentType() != null) {
return request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) ||
request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE);
}
return false;
}
}
上面代码中,json请求类型的参数,还需要下面这个包装类和过滤器,如果没有json请求的情况,下面的代码可以不要
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
/**
* 将request请求里面的inputStream复制一份出来放到固定参数里面
* huangdan
*/
public class MyRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public MyRequestWrapper(HttpServletRequest request) {
super(request);
String bodyStr = getBodyString(request);
body = bodyStr.getBytes(Charset.defaultCharset());
}
public String getBodyString(final ServletRequest request) {
try{
return inputStream2String(request.getInputStream());
}catch (IOException e){
throw new RuntimeException(e);
}
}
public String getBodyString() {
final InputStream inputStream = new ByteArrayInputStream(body);
return inputStream2String(inputStream);
}
/**
* 将inputStream里的数据读取出来并转换成字符串
*
* @param inputStream inputStream
* @return String
*/
private String inputStream2String(InputStream inputStream) {
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return sb.toString();
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
过滤器:
import com.hdan.happy.util.MyRequestWrapper;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 过滤器,为了将json请求的requestbody参数复制出来一份方便记录操作日志时取参数
*
*/
@Component
@WebFilter(
urlPatterns = {"/*"},
filterName = "replaceStreamFilter"
)
public class ReplaceStreamFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if(request instanceof HttpServletRequest && !ServletFileUpload.isMultipartContent((HttpServletRequest) request)){
ServletRequest requestWrapper = new MyRequestWrapper((HttpServletRequest) request);
filterChain.doFilter(requestWrapper, servletResponse);
}else{
filterChain.doFilter(request, servletResponse);
}
}
@Override
public void destroy() {
}
}
测试代码:
@RestController
@RequestMapping("test")
@Slf4j
public class TestController {
@PostMapping("saveArticle")
@OperLog(bizType="保存文章")
public Object saveArticle(String title, String content){
return true;
}
@PostMapping("saveArticle2")
@OperLog(bizType="保存文章2")
public Object saveArticle2(@RequestBody Map<String, Object> params){
return true;
}
}
请求这两个方法后,会分别输出: