SpringAOP获取请求信息
1.只获取请求参数
当需要在aop中获取请求的参数而无需获取响应参数,并做拦截时,可以参考下面的方法(原理很简单,使用@Before在方法执行之前获取参数):
package com.zxh.configure; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.*; @Aspect @Component @Slf4j public class ControllerAspect { private static ObjectMapper objectMapper = new ObjectMapper(); @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)") public void controllerGetMethodPointcut() { } @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)") public void controllerPostMethodPointcut() { } @Before("controllerGetMethodPointcut()") public void doBeforeGetMethod(JoinPoint joinPoint) throws Exception { Map<String, Object> requestParams = getRequestParams(joinPoint); } @Before("controllerPostMethodPointcut()") public void doBeforePostMethod(JoinPoint joinPoint) throws Exception { Map<String, Object> requestParams = this.getRequestParams(joinPoint); this.preHandle(requestParams); } private void preHandle(Map<String, Object> requestParams) { String uri = (String) requestParams.get("uri"); String method = (String) requestParams.get("method"); String params = ((List<String>) requestParams.get("params")).get(0); JSONObject headers = (JSONObject) requestParams.get("headers"); if (!HttpMethod.POST.toString().equals(method)) { throw new RuntimeException("方法不被允许"); } //写具体的业务,用于拦截。不符合条件时直接抛出异常,全局捕获异常进行处理 } //获取请求的相关信息 private Map<String, Object> getRequestParams(JoinPoint joinPoint) throws UnsupportedEncodingException { Map<String, Object> requestParams = new HashMap<>(); //获取请求信息 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); requestParams.put("uri", request.getRequestURI()); // 获取请求头信息,需要注意的是,请求头中的key都会转成小写 Enumeration<String> enumeration = request.getHeaderNames(); JSONObject headers = new JSONObject(); while (enumeration.hasMoreElements()) { String name = enumeration.nextElement(); String value = request.getHeader(name); headers.put(name, value); } requestParams.put("headers", headers); //获取请求的方法 String method = request.getMethod(); requestParams.put("method", method); List<String> params = new ArrayList<>(); if (HttpMethod.GET.toString().equals(method)) {// get请求 String queryString = request.getQueryString(); if (StringUtils.isNotBlank(queryString)) { params.add(0, URLDecoder.decode(queryString, "UTF-8")); } } else {//其他请求 Object[] paramsArray = joinPoint.getArgs(); if (paramsArray != null && paramsArray.length > 0) { for (int i = 0; i < paramsArray.length; i++) { if (paramsArray[i] instanceof Serializable || paramsArray[i] instanceof RequestFacade) { params.add(paramsArray[i].toString()); } else { try { //使用json系列化 反射等等方法 反系列化会影响请求性能建议重写toString方法实现系列化接口 String param = objectMapper.writeValueAsString(paramsArray[i]); if (StringUtils.isNotBlank(param)) params.add(param); } catch (JsonProcessingException e) { e.printStackTrace(); log.error("json序列化异常", e); } } } } } log.info(">>>>>>uri: {},method: {}", request.getRequestURI(), method); log.info(">>>>>>headers: {}", headers); log.info(">>>>>>params: {}", params); requestParams.put("params", params); return requestParams; } }
使用时需要导入aop的依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
分别请求下面的路径,进行测试
get请求:
日志打印结果
post请求
日志打印结果
2.获取请求体和响应体
有时需要记录请求的请求参数和响应参数时,可通过自定义注解方式,使用@Around进行环绕通知,它贯穿请求的整个过程。
示例如下,其中自定义注解名称为InterfaceLog
@Component @Aspect @Slf4j public class InterfaceLogAspect { private static ConcurrentHashMap<String, InterfaceRequestLogVO> data = new ConcurrentHashMap<>(); @Pointcut("@annotation(com.zxh.common.annotation.InterfaceLog)") public void interfaceLogAction() { } @Around("interfaceLogAction()") public Object aroundHandle(ProceedingJoinPoint joinPoint) throws Throwable { //日志对象 RequestLog reqLog = new IRequestLog(); //请求处理开始 reqLog.setRequestTime(new Date()); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); HttpServletRequest request = attributes.getRequest(); String requestURI = request.getRequestURI(); // 参数值 Object[] args = joinPoint.getArgs(); // 参数名数组 String[] argNames = signature.getParameterNames(); // 构造参数组集合 StringBuilder requestParams = new StringBuilder(); requestParams.append("{"); for (int i = 0; i < argNames.length; i++) { requestParams.append(argNames[i]).append(":").append(args[i].toString()).append(","); } if (requestParams.length() > 1) requestParams.deleteCharAt(requestParams.length() - 1); requestParams.append("}"); data.put("data", reqLog);//先设置请求的一些数据 //开始响应 Object requestBody = joinPoint.proceed();//返回体 //到这里请求已完成,成功时往下走,异常时执行后置异常通知中的代码 //这里只打印了日志,实际场景中可记录到日志中 log.info(">>>>>>请求体: {}", requestParams); log.info(">>>>>>响应体: {}", JSON.toJSONString(requestBody)); return requestBody; } /** * 后缀异常通知 * * @param joinPoint * @param e */ @AfterThrowing(value = "interfaceLogAction()", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, Exception e) { RequestLog l = data.get("data");//发生异常时获取请求体信息,可记录到日志中 log.error("发生异常:{}", e); }
}
在异常时也可以记录异常信息到日志中,根据实际情况而定。
就是这么简单,你学废了吗?感觉有用的话,给笔者点个赞吧 !