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);
    }
}

在异常时也可以记录异常信息到日志中,根据实际情况而定。

posted @ 2021-09-28 16:35  钟小嘿  阅读(2423)  评论(0编辑  收藏  举报