The HandlerExceptionResolver interface implements global exception capture

Use HandlerExceptionResolver interface to achieve global exception capture

Scenario : An exception occurs before entering our business interface, such as parameter parsing exception, interceptor processing exception, etc. At this time, the custom exception will not go through the interface because it has not gone through the interface.

 step

first step

Define the mvc global exception handling class AbstractWebExceptionHandler class implements 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;
    }

}

 Second step

API global exception handling class ApiExceptionHandler, extends AbstractWebExceptionHandler, rewrite method, handle exception information

 

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

}

 

Guess you like

Origin blog.csdn.net/qq_39564710/article/details/115323207