package com.simuwang.deploy.components; import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.util.StrUtil; import com.simuwang.base.common.exception.APIException; import com.simuwang.base.common.exception.ErrorInfo; import com.smppw.common.pojo.enums.status.ResultCode; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.ConstraintViolationException; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authz.UnauthenticatedException; import org.apache.shiro.authz.UnauthorizedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.context.properties.bind.validation.BindValidationException; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.validation.BindException; import org.springframework.validation.ObjectError; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.resource.NoResourceFoundException; import org.springframework.web.util.WebUtils; import java.time.LocalDateTime; import java.util.List; /** * @author wangzaijun * @date 2023/8/12 16:23 * @description 错误信息构造工具 */ @Component public class ErrorInfoBuilder implements HandlerExceptionResolver, Ordered { /** * 错误KEY */ private final static String ERROR_NAME = "simuwang.error"; private final Logger logger = LoggerFactory.getLogger(this.getClass()); // /** // * 错误配置(ErrorConfiguration) // */ // private final ErrorProperties errorProperties; // // /** // * 错误构造器 (Constructor) 传递配置属性:server.xx -> server.error.xx // */ // public ErrorInfoBuilder(ServerProperties serverProperties) { // this.errorProperties = serverProperties.getError(); // } /** * 构建错误信息.(ErrorInfo) */ public ErrorInfo getErrorInfo(HttpServletRequest request) { return getErrorInfo(request, getError(request)); } /** * 构建错误信息.(ErrorInfo) */ public ErrorInfo getErrorInfo(HttpServletRequest request, Throwable error) { String url = request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE).toString(); ErrorInfo errorInfo = new ErrorInfo(); errorInfo.setTime(LocalDateTime.now().toString()); errorInfo.setUrl(url); String msg; int code = getHttpStatus(request).value(); if (error instanceof NoResourceFoundException) { msg = "请求资源找不到"; } else if (error instanceof UnauthorizedException) { msg = "没有对应接口的权限"; } else if (error instanceof UnknownAccountException || error instanceof IncorrectCredentialsException) { msg = ResultCode.AUTH_FAILD.getMsg(); } else if (error instanceof AuthenticationException) { code = HttpStatus.UNAUTHORIZED.value(); msg = "登录认证失败"; } else if (error instanceof APIException e) { msg = e.getMsg(); code = e.getCode(); } else if (error instanceof UnauthenticatedException e) { msg = e.getMessage(); } else if (error instanceof HttpMessageNotReadableException || error instanceof BindValidationException) { msg = "请求参数错误"; } else if (error instanceof BindException e) { // 参数绑定或校验错误 List allErrors = e.getBindingResult().getAllErrors(); msg = allErrors.get(0).getDefaultMessage(); } else if (error instanceof ConstraintViolationException e) { // 约束验证错误 msg = e.getMessage(); } else { msg = "未知的错误"; } errorInfo.setError(msg); errorInfo.setStatusCode(code); errorInfo.setReasonPhrase(getHttpStatus(request).getReasonPhrase()); errorInfo.setStackTrace(ExceptionUtil.stacktraceToString(error)); logger.error(StrUtil.format("{} 接口请求错误:{}", url, errorInfo.getStackTrace())); return errorInfo; } /** * 获取错误.(Error/Exception) * * @see org.springframework.boot.web.servlet.error.DefaultErrorAttributes #addErrorDetails */ public Throwable getError(HttpServletRequest request) { //根据HandlerExceptionResolver接口方法来获取错误. Throwable error = (Throwable) request.getAttribute(ERROR_NAME); //根据Request对象获取错误. if (error == null) { error = (Throwable) request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE); } //当获取错误非空,取出RootCause. if (error != null) { while (error instanceof ServletException && error.getCause() != null) { error = error.getCause(); } } else { //当获取错误为null,此时我们设置错误信息即可. String message = (String) request.getAttribute(WebUtils.ERROR_MESSAGE_ATTRIBUTE); if (StringUtils.isEmpty(message)) { HttpStatus status = getHttpStatus(request); message = "Unknown Exception But " + status.value() + " " + status.getReasonPhrase(); } error = new Exception(message); } return error; } /** * 获取通信状态(HttpStatus) * * @see org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController #getStatus */ public HttpStatus getHttpStatus(HttpServletRequest request) { Integer statusCode = (Integer) request.getAttribute(WebUtils.ERROR_STATUS_CODE_ATTRIBUTE); try { return statusCode != null ? HttpStatus.valueOf(statusCode) : HttpStatus.INTERNAL_SERVER_ERROR; } catch (Exception ex) { return HttpStatus.INTERNAL_SERVER_ERROR; } } // /** // * 获取堆栈轨迹(StackTrace) // * // * @see org.springframework.boot.web.servlet.error.DefaultErrorAttributes #addStackTrace // */ // public String getStackTraceInfo(Throwable error, boolean flag) { // if (!flag) { // return "omitted"; // } // StringWriter stackTrace = new StringWriter(); // error.printStackTrace(new PrintWriter(stackTrace)); // stackTrace.flush(); // return stackTrace.toString(); // } // /** // * 判断是否包含堆栈轨迹.(isIncludeStackTrace) // * // * @see org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController #isIncludeStackTrace // */ // public boolean isIncludeStackTrace(HttpServletRequest request) { // // //读取错误配置(server.error.include-stacktrace=NEVER) // ErrorProperties.IncludeAttribute includeStacktrace = errorProperties.getIncludeStacktrace(); // // //情况1:若IncludeStacktrace为ALWAYS // if (includeStacktrace == ErrorProperties.IncludeAttribute.ALWAYS) { // return true; // } // //情况2:若请求参数含有trace // if (includeStacktrace == ErrorProperties.IncludeAttribute.ON_PARAM) { // String parameter = request.getParameter("trace"); // return parameter != null && !"false".equalsIgnoreCase(parameter); // } // //情况3:其它情况 // return false; // } /** * 保存错误/异常. * * @see org.springframework.web.servlet.DispatcherServlet #processHandlerException 进行选举HandlerExceptionResolver */ @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { request.setAttribute(ERROR_NAME, ex); return null; } /** * 提供优先级 或用于排序 */ @Override public int getOrder() { return HIGHEST_PRECEDENCE; } }