|
@@ -0,0 +1,171 @@
|
|
|
|
+package com.simuwang.deploy.components;
|
|
|
|
+
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
|
+import com.simuwang.base.common.exception.ErrorInfo;
|
|
|
|
+import jakarta.servlet.ServletException;
|
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
|
+import jakarta.servlet.http.HttpServletResponse;
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
+import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
|
|
|
+import org.springframework.boot.autoconfigure.web.ServerProperties;
|
|
|
|
+import org.springframework.core.Ordered;
|
|
|
|
+import org.springframework.http.HttpStatus;
|
|
|
|
+import org.springframework.lang.Nullable;
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
+import org.springframework.web.servlet.HandlerExceptionResolver;
|
|
|
|
+import org.springframework.web.servlet.ModelAndView;
|
|
|
|
+import org.springframework.web.util.WebUtils;
|
|
|
|
+
|
|
|
|
+import java.io.PrintWriter;
|
|
|
|
+import java.io.StringWriter;
|
|
|
|
+import java.time.LocalDateTime;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * @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);
|
|
|
|
+ errorInfo.setError(error.getMessage());
|
|
|
|
+ errorInfo.setStatusCode(getHttpStatus(request).value());
|
|
|
|
+ errorInfo.setReasonPhrase(getHttpStatus(request).getReasonPhrase());
|
|
|
|
+ errorInfo.setStackTrace(getStackTraceInfo(error, isIncludeStackTrace(request)));
|
|
|
|
+ logger.error(StrUtil.format("{} 接口请求错误:{}", url, errorInfo.getError()));
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+}
|