HandlerExceptionResolverインターフェイスは、グローバル例外キャプチャを実装します

HandlerExceptionResolverインターフェイスを使用して、グローバル例外キャプチャ実現します

シナリオ:パラメーター解析例外、インターセプター処理例外など、ビジネスインターフェイスに入る前に例外が発生します。現時点では、カスタム例外はインターフェイスをまだ通過していないため、インターフェイスを通過しません。

 ステップ

最初の一歩

mvcグローバル例外処理クラスを定義しますAbstractWebExceptionHandlerクラスはHandlerExceptionResolverを実装します

package com.xihongshi.common.handler;

import com.alibaba.fastjson.JSON;
import com.xihongshi.common.constants.Constant;
import com.xihongshi.common.constants.LogConstant;
import com.xihongshi.common.exception.InvalidSessionException;
import com.xihongshi.utils.HttpResponseUtil;
import com.xihongshi.utils.HttpServletUtil;
import com.xihongshi.utils.IpUtil;
import com.xihongshi.utils.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.slf4j.MDC;
import org.springframework.core.MethodParameter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.Map;

/**
 * @Description: mvc全局异常处理
 * @author: AndrewLee
 * @date: 2021-03-28
 */
@Slf4j
public abstract class AbstractWebExceptionHandler implements HandlerExceptionResolver {
    /**
     * 缓存的请求body
     */
    protected static final String CACHED_REQUESTBODY = "cached_requestBody";

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
        ModelAndView mv = new ModelAndView();

        // 业务逻辑异常
        Object resultModel = doResolveException(request, response, handler, e);

        logBadRequest(e, request, handler, resultModel);

        writeErrorResponse(request, response, mv, handler, resultModel);

        clearMdc();
        clearThreadLocal();
        return mv;
    }

    protected abstract Object doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e);

    /**
     * 记录 Request 异常日志(方便排查问题)
     *
     * @param e
     * @param resultModel
     */
    protected void logBadRequest(Throwable e, HttpServletRequest request, Object handler, Object resultModel) {
        try {
            String requestBody = getRequestBody(request);
            Map<String, String> requestParams = HttpServletUtil.getRequestParams(request);
            Map<String, String> headerMap = HttpServletUtil.getHeaderMap(request);
            String responseStr = toString(resultModel);
            String clientIp = IpUtil.getClientIp(request);
            String exceptionName = e.getClass().getSimpleName();
            String exceptionCauseName = (e.getCause() == null ? null : e.getCause().getClass().getSimpleName());
            String exceptionMessage = e.getMessage();
            long costTime = getCostTime(request);

            String errorMessage = "logBadRequest -> Exception=%s - %s - %s -> clientIp=%s ,url=%s ,method=%s ,contentType=%s -> requestParams=%s -> requestBody=%s -> costTime=%s ,responseStr=%s -> header=%s";
            errorMessage = String.format(errorMessage
                    , new Object[]{exceptionName, exceptionCauseName, exceptionMessage,
                            clientIp, request.getRequestURL(), request.getMethod(), request.getContentType(), requestParams.toString(), requestBody, costTime, responseStr, headerMap});

            if (isIgnoreException(e)) {
                //可以忽略的异常(不打印堆栈信息)
                log.error(errorMessage);
            } else {
                log.error(errorMessage, e);
            }

        } catch (Exception ex) {
            log.warn("[全局异常处理] logBadRequest error: " + ex.getMessage(), ex);
        }
    }

    /**
     * @param request
     * @return
     */
    private long getCostTime(HttpServletRequest request) {
        long costTime = 0;
        Long requestStartTime = (Long) request.getAttribute(LogConstant.REQUEST_START_TIME_ATTR);
        if (requestStartTime != null) {
            costTime = System.currentTimeMillis() - requestStartTime;
        }
        return costTime;
    }

    protected String toString(Object resultModel) {
        if (null == resultModel) {
            return null;
        }
        String str = null;

        if (resultModel instanceof Serializable) {
            str = JSON.toJSONString(resultModel);
        } else {
            str = resultModel.toString();
        }
        return str;
    }

    /**
     * 输出异常响应
     *
     * @param request
     * @param response
     * @param mv
     * @param resultModel
     */
    protected void writeErrorResponse(HttpServletRequest request, HttpServletResponse response
            , ModelAndView mv, Object handler, Object resultModel) {
        if (null == resultModel) {
            return;
        }
        if (!isApiEntityReturnType(handler)) {
            log.warn("[警告]当前接口[{}]返回类型{}非接口统一响应格式,将不返回统一的异常响应!", request.getRequestURL(), getMethodReturnType(handler));
            return;
        }
        String responseJson = JSON.toJSONString(resultModel);
        try {
            String requestUrl = request.getRequestURL().toString();
            String requestBody = getRequestBody(request);
            String requestIp = IpUtil.getClientIp(request);
            log.info("writeErrorResponse -> clientIp={} ,url={} ,param={} -> respStr={}", requestIp, requestUrl, requestBody, responseJson);
        } catch (Exception ex) {
            log.warn("writeErrorResponse error: " + ex.getMessage(), ex);
        }
        HttpResponseUtil.renderJson(response, responseJson);
    }

    /**
     * 可以忽略的异常(不打印堆栈信息)
     *
     * @param e
     * @return
     */
    private boolean isIgnoreException(Throwable e) {
        if (e instanceof InvalidSessionException) {
            return true;
        }
        return false;
    }

    protected boolean isApiEntityReturnType(Object handler) {
        return isResponseEntityReturnType(handler);
    }


    protected boolean isResponseEntityReturnType(Object handler) {
        Class<?> parameterType = getMethodReturnType(handler);
        return null != parameterType && ResponseResult.class.isAssignableFrom(parameterType);
    }

    private Class getMethodReturnType(Object handler) {
        if (null != handler && handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            MethodParameter returnType = handlerMethod.getReturnType();
            Class<?> parameterType = returnType.getParameterType();
            return parameterType;
        }
        return null;
    }

    /**
     * 清理日志上下文
     */
    private void clearMdc() {
        try {
            MDC.clear();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 清理线程本地变量
     */
    protected void clearThreadLocal() {

    }

    private String getRequestBody(HttpServletRequest request) throws IOException {
        String requestBody = (String) request.getAttribute(CACHED_REQUESTBODY);
        if (null == requestBody) {
            try {
                requestBody = IOUtils.toString(request.getInputStream(), Charset.forName(Constant.UTF8));
                request.setAttribute(CACHED_REQUESTBODY, requestBody);
            } catch (Exception e) {
                log.warn("getRequestBody error: " + e.getMessage(), e);
            }
        }
        return requestBody;
    }

}

 第二段階

APIグローバル例外処理クラスApiExceptionHandler、AbstractWebExceptionHandlerの拡張、メソッドの書き換え、例外情報の処理

 

package com.xihongshi.common.handler;


import com.xihongshi.common.exception.BizException;
import com.xihongshi.utils.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Description: API 全局异常处理
 * @author: AndrewLee
 * @date: 2021-03-28
 */
@Slf4j
public class ApiExceptionHandler extends AbstractWebExceptionHandler {
    public ApiExceptionHandler() {
        log.info("[Api全局异常拦截处理器]初始化{}", this);
    }

    @Override
    protected Object doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
        if (!isResponseEntityReturnType(handler)) {
            return null;
        }

        String errorCode = "-1";
        String errorMsg = "系统繁忙,请稍后再试";

        if (e instanceof BizException) {
            BizException ex = (BizException) e;
            errorCode = ex.getCode();
            errorMsg = ex.getMessage();
        }
        // 参数校验异常
        else if (e instanceof BindException) {
            BindingResult bindingResult = ((BindException) e).getBindingResult();
            if (bindingResult.hasErrors()) {
                errorMsg = (bindingResult.getAllErrors().get(0).getDefaultMessage());
            }
        } else if (e instanceof MethodArgumentNotValidException) {
            BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
            if (bindingResult.hasErrors()) {
                errorMsg = bindingResult.getAllErrors().get(0).getDefaultMessage();
            }
        }

        ResponseResult responseResult = new ResponseResult();
        responseResult.setCode(errorCode);
        responseResult.setMsg(errorMsg);
        return responseResult;
    }

}

 

おすすめ

転載: blog.csdn.net/qq_39564710/article/details/115323207