在SpringBoot中,请求参数使用了@Valid注解,同时使用Aop记录了日志,但是当发生@Valid的异常时,在Aop的切点中记录不到,虽然全局异常捕获到了@Valid异常,但在aop中无法处理异常。为了在Aop中记录请求参数和参数校验结果,需要将@Valid的BindingResult带入到aop中。
1. 注意点
- 方法的参数中若有@Valid注解,要绑定上
BindingResult bindingResult
BindingResult
-- 若不添加BindingResult ,当在参数实体中校验出错误时,就会直接抛出异常,程序走不到这里,也就不会走aop;
-- 添加BindingResult后,校验出错误时,会记录到result中,不抛出异常,程序走到这里时进aop,在aop中处理BindingResult结果
- 方法的参数中若有@Valid注解,要绑定上
- @Around注解上 要加上
&& args(.., bindingResult)
用来接收参数
image.png
- @Around注解上 要加上
2. Aop中处理@Valid校验结果
Aop示例中有接收swagger注解的处理
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.test.dao.LogDao;
import com.test.entity.Log;
import com.test.exception.MyArgumentException;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@Aspect
@Component
@Slf4j
public class LogPrintAspect {
@Resource
private LogDao logDao;
@Pointcut("within(com.test..*) && @within(org.springframework.web.bind.annotation.RestController)")
public void getParams() {
}
@Pointcut("within(com.test..*) && @within(org.springframework.web.bind.annotation.RestController)")
public void printLog() {
}
@Pointcut("within(com.test..*) && @within(org.springframework.web.bind.annotation.RestController) && !args(..,org.springframework.validation.BindingResult)")
public void printLogNoBindingResult() {
}
@Before(value = "getParams()")
public void beforeFunction(JoinPoint joinPoint) {
}
@AfterReturning(value = "printLog()", returning = "ret")
public void afterReturningFunction(Object ret) {
}
/**
* 此方法用来拦截不含BindingResult参数校验结果的方法
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("printLogNoBindingResult()")
public Object aroundFunction(ProceedingJoinPoint joinPoint) throws Throwable {
Long startTime = System.currentTimeMillis();
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
Log l = new Log();
l.setAccessTime(new Date(startTime));
l.setAccessMethod(joinPoint.getSignature().getName());
log.info("\r\n请求地址:" + request.getRequestURL().toString());
l.setRequestUri(request.getRequestURL().toString());
// 获取header
Map<String, String> headerMap = ServletUtil.getHeaderMap(request);
if (!headerMap.isEmpty()) {
log.info("\r\n请求头header:{}", headerMap);
}
// 获取表单请求参数
Map<String, String> m = ServletUtil.getParamMap(request);
if (m.isEmpty()) { // 无表单参数,获取其他参数
Object[] args = joinPoint.getArgs();
ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String[] parameterNames = pnd.getParameterNames(method);
for (int i = 0; i < parameterNames.length; i++) {
if (!"bindingResult".equals(parameterNames[i])) {
m.put(parameterNames[i], args[i].toString());
}
}
}
log.info(m.isEmpty()?"\r\n请求参数为空":"\r\n请求参数:{}", JSONUtil.parseObj(m).toJSONString(1));
l.setRequestParams(JSONUtil.parseObj(m).toJSONString(0));
l.setUserid(request.getHeader("CurrentUser"));
l.setRemoteIpAddress(request.getRemoteAddr());
// 获取@Api注解信息
Class<?> targetClass = joinPoint.getTarget().getClass();
if (targetClass.isAnnotationPresent(Api.class)) {
Api swaggerApi = (Api) targetClass.getDeclaredAnnotation(Api.class);
// log.info("模块名称-->> {}", swaggerApi.tags()[0]);
l.setAccessModule(swaggerApi.tags()[0]);
}
// 获取@ApiOperation注解信息
Signature sig = joinPoint.getSignature();
MethodSignature msig = (MethodSignature)sig;
Method md = targetClass.getMethod(msig.getName(), msig.getParameterTypes());
if (md.isAnnotationPresent(ApiOperation.class)) {
ApiOperation swaggerApiOperation = (ApiOperation) md.getDeclaredAnnotation(ApiOperation.class);
// log.info("功能名称-->> {}", swaggerApiOperation.value());
l.setAccessFunction(swaggerApiOperation.value());
}
// 执行proceed(),只能执行一次
Object result = joinPoint.proceed();
l.setResponseData(JSONUtil.parseObj(result).toJSONString(0));
log.info("\r\n返回结果: " + JSONUtil.parseObj(result));
Long endTime = System.currentTimeMillis();
l.setTimeConsuming((int)(endTime - startTime));
logDao.insert(l);
return result;
}
/**
* 此方法用来拦截含有BindingResult参数校验结果的方法
* @param joinPoint
* @param bindingResult
* @return
* @throws Throwable
*/
@Around("printLog() && args(.., bindingResult)")
public Object aroundFunctionWithBindingResult(ProceedingJoinPoint joinPoint, BindingResult bindingResult) throws Throwable {
Long startTime = System.currentTimeMillis();
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
Log l = new Log();
l.setAccessTime(new Date(startTime));
l.setAccessMethod(joinPoint.getSignature().getName());
log.info("\r\n请求地址:" + request.getRequestURL().toString());
l.setRequestUri(request.getRequestURL().toString());
// 获取header
Map<String, String> headerMap = ServletUtil.getHeaderMap(request);
if (!headerMap.isEmpty()) {
log.info("\r\n请求头header:{}", headerMap);
}
// 获取表单请求参数
Map<String, String> m = ServletUtil.getParamMap(request);
if (m.isEmpty()) { // 无表单参数,获取其他参数
Object[] args = joinPoint.getArgs();
ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String[] parameterNames = pnd.getParameterNames(method);
for (int i = 0; i < parameterNames.length; i++) {
if (!"bindingResult".equals(parameterNames[i])) {
m.put(parameterNames[i], args[i].toString());
}
}
}
log.info(m.isEmpty()?"\r\n请求参数为空":"\r\n请求参数:{}", JSONUtil.parseObj(m).toJSONString(1));
l.setRequestParams(JSONUtil.parseObj(m).toJSONString(0));
l.setUserid(request.getHeader("CurrentUser"));
l.setRemoteIpAddress(request.getRemoteAddr());
// 参数校验有错误返回
if (bindingResult.hasErrors()) {
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
List<String> lists = new ArrayList<String>();
for (FieldError error : fieldErrors) {
lists.add(error.getDefaultMessage());
}
l.setResponseData(lists.toString());
throw new MyArgumentException(lists.toString(), JSONUtil.parseObj(m));
}
// 获取@Api注解信息
Class<?> targetClass = joinPoint.getTarget().getClass();
if (targetClass.isAnnotationPresent(Api.class)) {
Api swaggerApi = (Api) targetClass.getDeclaredAnnotation(Api.class);
// log.info("模块名称-->> {}", swaggerApi.tags()[0]);
l.setAccessModule(swaggerApi.tags()[0]);
}
// 获取@ApiOperation注解信息
Signature sig = joinPoint.getSignature();
MethodSignature msig = (MethodSignature)sig;
Method md = targetClass.getMethod(msig.getName(), msig.getParameterTypes());
if (md.isAnnotationPresent(ApiOperation.class)) {
ApiOperation swaggerApiOperation = (ApiOperation) md.getDeclaredAnnotation(ApiOperation.class);
// log.info("功能名称-->> {}", swaggerApiOperation.value());
l.setAccessFunction(swaggerApiOperation.value());
}
// 执行proceed(),只能执行一次
Object result = joinPoint.proceed();
l.setResponseData(JSONUtil.parseObj(result).toJSONString(0));
log.info("\r\n返回结果: " + JSONUtil.parseObj(result));
Long endTime = System.currentTimeMillis();
l.setTimeConsuming((int)(endTime - startTime));
logDao.insert(l);
return result;
}
}
在Aop中处理校验结果,拿到自定义的message,抛出异常,由全局统一异常处理。
Springboot添加AOP参考:Springboot添加AOP打印请求参数 - 简书 (jianshu.com)
SpringBoot添加全局统一异常参考:SpringBoot添加全局统一异常处理 - 简书 (jianshu.com)
网友评论