天天看點

Spring Boot aop 擷取HttpResponse的狀态碼

Spring Boot aop 擷取HttpResponse的狀态碼方法如下:

HttpServletResponse httpServletResponse = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
int status = httpServletResponse.getStatus();
           

Spring Boot 項目中,@Around、@AfterReturning、@After注解的Pointcut不能擷取到程式異常時傳回的HTTPServletResponse的狀态碼,因為這幾個注解執行的條件均是在你調用的接口執行完成之後再執行(不論你的接口調用會不會抛異常)。如果你的接口沒問題,正常傳回,則上述方法擷取到的status為200正常值;如果你的接口抛異常,則該status仍為200,此時時錯誤的。

原因(個人了解):

當你發送HTTPServletRequest伺服器接收到之後,會生成一個HTTPServletResponse對象,這個response對象預設的status初始值為200,如果你的程式内部抛異常了,Java内部會根據你的異常原因給status指派(比如null空指針異常,status會被指派為500)。

而使用@Around、@AfterReturning、@After注解時,無論你接口的程式代碼是否正常執行,都會被認為該接口調用已經執行完成,你的接口代碼出錯時會被認為傳回一個null,由于調用接口是在你aop代碼的@Around或@AfterReturning或@After注解中執行完成的,是以初始化的HTTPServletResponse對象的status值并未做修改(因為你的接口正常調用完成後,Java内部會根據實際情況給status指派,而上述幾個注解加入後,感覺像是跳過了異常(或者說用接口傳回的 null處理了異常),status貌似未被賦上錯誤代碼值)。

是以HTTPServletResponse的status值始終為200,即Http的請求與傳回總是正常執行完(程式中的異常在aop代碼中被捕獲到,但未處理)。

最後強調一下:Http請求與傳回是否正常執行 與 你的接口代碼是否正常執行,這是兩個概念。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

@Aspect
@Component
public class SysLoggerAspect {
    @Autowired
    private LoggerService loggerService;

    @Pointcut("@annotation(com.ytkj.common.annotation.SysLogger)")
    public void loggerPointCut() {

    }

    //@Before為loggerPointCut()執行前,即@SysLogger注解的processRecovery方法執行前。
    //此時擷取不到執行後的結果,即狀态碼、傳回結果、運作時間無法擷取。故用@Around注解
//    @Before("loggerPointCut()")
//    public void saveSysLog(JoinPoint joinPoint) {
//        PreproRecoveryLog preproRecoveryLog = new PreproRecoveryLog();
//        //請求的參數
//        Object[] args = joinPoint.getArgs();
//        String params="";
//        for(Object o:args){
//            params+=JSON.toJSONString(o);
//        }
//        if(!StringUtils.isEmpty(params)) {
//            preproRecoveryLog.setParams(params);
//        }
//        //設定IP位址
//        preproRecoveryLog.setIp(HttpUtils.getIpAddress());
//        //設定寫入日志時間
//        preproRecoveryLog.setCreateDate(new Date());
//
//        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        HttpServletResponse response = requestAttributes.getResponse();
//        System.out.println(response);
//        System.out.println(response.getStatus());
//
//        //儲存系統日志
//        System.out.println("********aop out success*********");
//        loggerService.log(preproRecoveryLog);
//    }

    @Around("loggerPointCut()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        PreproRecoveryLog preproRecoveryLog = new PreproRecoveryLog();
        //請求的參數
        Object[] args = proceedingJoinPoint.getArgs();
        String params="";
        for(Object o:args){
            params+= JSON.toJSONString(o);
        }
        if(!StringUtils.isEmpty(params)) {
            preproRecoveryLog.setParams(params);
        }
        //設定IP位址
        preproRecoveryLog.setIp(HttpUtils.getIpAddress());
        //設定寫入日志時間
        Date createDate = new Date();
        preproRecoveryLog.setCreateDate(createDate);
        long startTime = System.currentTimeMillis();
//        System.out.println("********aop out success1*********");

        Object result = null;
        try {
            //以上為loggerPointCut()執行前,即@SysLogger注解的processRecovery方法執行前
            result = proceedingJoinPoint.proceed();
            //以下為loggerPointCut()執行後,即@SysLogger注解的processRecovery方法執行後

            //擷取并設定調用接口運作時長
            long runTime = System.currentTimeMillis() - startTime;
            preproRecoveryLog.setRunTime(runTime);

            //擷取并設定HttpRequest對應HttpResponse的狀态碼
            int status = HttpUtils.getHttpServletResponse().getStatus();
            preproRecoveryLog.setStatusCode(status+"");

            //擷取result并轉換為JSON字元串  設定寫入日志的結果
            String respResult = JSONObject.toJSONString(result);
            preproRecoveryLog.setResult(respResult);
        } catch (Exception e){
            //如果程式執行出錯,捕獲到異常并記錄狀态碼為-1
//            int status = HttpUtils.getHttpServletResponse().getStatus();
//            System.out.println(status);
            preproRecoveryLog.setStatusCode("-1");
            System.out.println(e.getMessage());
            e.printStackTrace();
        }

        //儲存系統日志
//        System.out.println("********aop out success*********");
        loggerService.log(preproRecoveryLog);
        return result;
    }

}
           

HttpUtils.java

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.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;


public class HttpUtils {


    /**
     * 嘗試擷取目前請求的HttpServletRequest執行個體
     *
     * @return HttpServletRequest
     */
    public static HttpServletRequest getHttpServletRequest() {
        try {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 嘗試擷取目前請求的HttpServletResponse執行個體
     *
     * @return HttpServletResponse
     */
    public static HttpServletResponse getHttpServletResponse() {
        try {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        } catch (Exception e) {
            return null;
        }
    }

    public static Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> map = new LinkedHashMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        while (enumeration.hasMoreElements()) {
            String key = enumeration.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }

    /**
     * 擷取請求用戶端的真實ip位址
     *
     * @param request 請求對象
     * @return ip位址
     */
    public static String getIpAddress(HttpServletRequest request) {

        // 擷取請求主機IP位址,如果通過代理進來,則透過防火牆擷取真實IP位址
        String ip = request.getHeader("X-Forwarded-For");

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } else if (ip.length() > 15) {
            String[] ips = ip.split(",");
            for (int index = 0; index < ips.length; index++) {
                String strIp = (String) ips[index];
                if (!("unknown".equalsIgnoreCase(strIp))) {
                    ip = strIp;
                    break;
                }
            }
        }
        return ip;
    }

    /**
     * 擷取請求用戶端的真實ip位址
     *
     * @param
     * @return ip位址
     */
    public static String getIpAddress() {
        // 擷取請求主機IP位址,如果通過代理進來,則透過防火牆擷取真實IP位址
        return getIpAddress(getHttpServletRequest());
    }


}

           

附上幾個我在查資料時的連結:

https://bbs.csdn.net/topics/391865861?page=1

https://bbs.csdn.net/topics/390675717

https://blog.csdn.net/DuShiWoDeCuo/article/details/78818413

https://blog.csdn.net/puhaiyang/article/details/78146620