package cn.stylefeng.guns.core.error;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
import cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum;
import cn.stylefeng.roses.kernel.demo.exception.DemoException;
import cn.stylefeng.roses.kernel.demo.exception.enums.DemoExceptionEnum;
import cn.stylefeng.roses.kernel.rule.constants.SymbolConstant;
import cn.stylefeng.roses.kernel.rule.exception.AbstractExceptionEnum;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.roses.kernel.rule.exception.enums.defaults.DefaultBusinessExceptionEnum;
import cn.stylefeng.roses.kernel.rule.pojo.response.ErrorResponseData;
import cn.stylefeng.roses.kernel.rule.util.ExceptionUtil;
import cn.stylefeng.roses.kernel.rule.util.HttpServletUtil;
import cn.stylefeng.roses.kernel.rule.util.ProjectUtil;
import cn.stylefeng.roses.kernel.rule.util.ResponseRenderUtil;
import cn.stylefeng.roses.kernel.validator.api.exception.ParamValidateException;
import cn.stylefeng.roses.kernel.validator.api.exception.enums.ValidatorExceptionEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.exceptions.PersistenceException;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.ui.Model;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ValidationException;
import java.util.List;
import static cn.stylefeng.guns.core.consts.ProjectConstants.ROOT_PACKAGE_NAME;
/**
* 全局异常处理器,拦截控制器层的异常
*
* @author fengshuonan
* @since 2020/12/16 14:20
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 请求参数缺失异常
*
* @author fengshuonan
* @since 2020/12/16 14:20
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData<?> missingParam(MissingServletRequestParameterException missingServletRequestParameterException) {
String parameterName = missingServletRequestParameterException.getParameterName();
String parameterType = missingServletRequestParameterException.getParameterType();
return renderJson(ValidatorExceptionEnum.MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION, parameterName, parameterType);
}
/**
* HttpMessageConverter转化异常,一般为json解析异常
*
* @author fengshuonan
* @since 2020/12/16 14:21
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData<?> httpMessageNotReadable(HttpMessageNotReadableException httpMessageNotReadableException) {
log.error("参数格式传递异常,具体信息为:{}", httpMessageNotReadableException.getMessage());
return renderJson(ValidatorExceptionEnum.HTTP_MESSAGE_CONVERTER_ERROR);
}
/**
* 拦截不支持媒体类型异常
*
* @author fengshuonan
* @since 2020/12/16 14:26
*/
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData<?> httpMediaTypeNotSupport(HttpMediaTypeNotSupportedException httpMediaTypeNotSupportedException) {
log.error("参数格式传递异常,具体信息为:{}", httpMediaTypeNotSupportedException.getMessage());
return renderJson(ValidatorExceptionEnum.HTTP_MEDIA_TYPE_NOT_SUPPORT);
}
/**
* 不受支持的http method
*
* @author fengshuonan
* @since 2020/12/16 14:56
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData<?> methodNotSupport(HttpServletRequest request) {
String httpMethod = request.getMethod().toUpperCase();
return renderJson(ValidatorExceptionEnum.HTTP_METHOD_NOT_SUPPORT, httpMethod);
}
/**
* 404找不到资源
*
* @author fengshuonan
* @since 2020/12/16 14:58
*/
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData<?> notFound(NoHandlerFoundException e) {
return renderJson(ValidatorExceptionEnum.NOT_FOUND);
}
/**
* 请求参数校验失败,拦截 @Valid 校验失败的情况
*
* @author fengshuonan
* @since 2020/12/16 14:59
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData<?> methodArgumentNotValidException(MethodArgumentNotValidException e) {
String bindingResult = getArgNotValidMessage(e.getBindingResult());
return renderJson(ValidatorExceptionEnum.VALIDATED_RESULT_ERROR, bindingResult);
}
/**
* 请求参数校验失败,拦截 @Validated 校验失败的情况
* <p>
* 两个注解 @Valid 和 @Validated 区别是后者可以加分组校验,前者没有分组校验
*
* @author fengshuonan
* @since 2020/12/16 15:08
*/
@ExceptionHandler(BindException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData<?> bindException(BindException e) {
String bindingResult = getArgNotValidMessage(e.getBindingResult());
return renderJson(ValidatorExceptionEnum.VALIDATED_RESULT_ERROR, bindingResult);
}
/**
* 拦截 @TableUniqueValue 里抛出的异常
*
* @author fengshuonan
* @since 2020/12/26 14:05
*/
@ExceptionHandler(ValidationException.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponseData<?> bindException(ValidationException e) {
if (e.getCause() instanceof ParamValidateException) {
ParamValidateException paramValidateException = (ParamValidateException) e.getCause();
return renderJson(paramValidateException.getErrorCode(), paramValidateException.getUserTip());
}
return renderJson(e);
}
/**
* 拦截全校校验一类的异常
* <p>
* 这里重点做一类特殊处理,也就是对过期登录用用户
* <p>
* 如果用户登录过期,并且为ajax请求,则response的header增加session-timeout的标识
* <p>
* 如果用户登录过期,不是ajax请求,则直接跳转到登录页面,并提示会话超时
*
* @author fengshuonan
* @since 2020/12/16 15:11
*/
@ExceptionHandler(AuthException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String authError(AuthException authException, HttpServletRequest request, HttpServletResponse response, Model model) {
String errorCode = authExcept