La interfaz HandlerExceptionResolver implementa la captura de excepciones globales

Utilice la interfaz HandlerExceptionResolver para lograr la captura de excepciones globales

Escenario : Se produce una excepción antes de ingresar a nuestra interfaz comercial, como una excepción de análisis de parámetros, una excepción de procesamiento de interceptor, etc. En este momento, la excepción personalizada no pasará por la interfaz porque aún no ha pasado la interfaz.

 paso

primer paso

Definir la clase de manejo de excepciones globales de mvc. La clase AbstractWebExceptionHandler implementa 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;
    }

}

 Segundo paso

Clase de manejo de excepciones globales de API ApiExceptionHandler, extiende AbstractWebExceptionHandler, método de reescritura, maneja información de excepciones

 

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

}

 

Supongo que te gusta

Origin blog.csdn.net/qq_39564710/article/details/115323207
Recomendado
Clasificación