using NCC.Dependency; using NCC.DynamicApiController; using NCC.Extensions; using NCC.Localization; using NCC.Templates.Extensions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading; namespace NCC.FriendlyException { /// /// 抛异常静态类 /// [SuppressSniffer] public static class NCCException { /// /// 方法错误异常特性 /// private static readonly ConcurrentDictionary ErrorMethods; /// /// 错误代码类型 /// private static readonly IEnumerable ErrorCodeTypes; /// /// 错误消息字典 /// private static readonly ConcurrentDictionary ErrorCodeMessages; /// /// 友好异常设置 /// private static readonly FriendlyExceptionSettingsOptions _friendlyExceptionSettings; /// /// 构造函数 /// static NCCException() { ErrorMethods = new ConcurrentDictionary(); _friendlyExceptionSettings = App.GetOptions(); ErrorCodeTypes = GetErrorCodeTypes(); ErrorCodeMessages = GetErrorCodeMessages(); } /// /// 抛出业务异常日志 /// /// 异常消息 /// String.Format 参数 /// 异常实例 public static AppFriendlyException Bah(string errorMessage, params object[] args) { var friendlyException = Oh(errorMessage, typeof(ValidationException), args).StatusCode(StatusCodes.Status400BadRequest); friendlyException.ValidationException = true; return friendlyException; } /// /// 抛出业务异常日志 /// /// 错误码 /// String.Format 参数 /// 异常实例 public static AppFriendlyException Bah(object errorCode, params object[] args) { var friendlyException = Oh(errorCode, typeof(ValidationException), args).StatusCode(StatusCodes.Status400BadRequest); friendlyException.ValidationException = true; return friendlyException; } /// /// 抛出业务异常日志 自定义状态码 /// /// 状态码 /// 消息 /// String.Format 参数 /// 异常实例 public static AppFriendlyException BahStatus(string errorMessage, int statusCode = StatusCodes.Status400BadRequest, params object[] args) { var friendlyException = Oh(errorMessage, typeof(ValidationException), args).StatusCode(statusCode); friendlyException.ValidationException = true; return friendlyException; } /// /// 抛出字符串异常 /// /// 异常消息 /// String.Format 参数 /// 异常实例 public static AppFriendlyException Oh(string errorMessage, params object[] args) { return new AppFriendlyException(MontageErrorMessage(errorMessage, default, args), default); } /// /// 抛出字符串异常 /// /// 异常消息 /// 具体异常类型 /// String.Format 参数 /// 异常实例 public static AppFriendlyException Oh(string errorMessage, Type exceptionType, params object[] args) { var exceptionMessage = MontageErrorMessage(errorMessage, default, args); return new AppFriendlyException(exceptionMessage, default, Activator.CreateInstance(exceptionType, new object[] { exceptionMessage }) as Exception); } /// /// 抛出错误码异常 /// /// 错误码 /// String.Format 参数 /// 异常实例 public static AppFriendlyException Oh(object errorCode, params object[] args) { return new AppFriendlyException(GetErrorCodeMessage(errorCode, args), errorCode); } /// /// 抛出错误码异常 /// /// 错误码 /// 具体异常类型 /// String.Format 参数 /// 异常实例 public static AppFriendlyException Oh(object errorCode, Type exceptionType, params object[] args) { var exceptionMessage = GetErrorCodeMessage(errorCode, args); return new AppFriendlyException(exceptionMessage, errorCode, Activator.CreateInstance(exceptionType, new object[] { exceptionMessage }) as Exception); } /// /// 重试有异常的方法,还可以指定特定异常 /// /// /// 重试次数 /// 重试间隔时间 /// 异常类型,可多个 public static void Retry(Action action, int numRetries, int retryTimeout, params Type[] exceptionTypes) { if (action == null) throw new ArgumentNullException(nameof(action)); _ = Retry(() => { action(); return 0; }, numRetries, retryTimeout, exceptionTypes); } /// /// 重试有异常的方法,还可以指定特定异常 /// /// /// /// 重试次数 /// 重试间隔时间 /// 异常类型,可多个 public static T Retry(Func action, int numRetries, int retryTimeout, params Type[] exceptionTypes) { if (action == null) throw new ArgumentNullException(nameof(action)); // 不断重试 while (true) { try { return action(); } catch (Exception ex) { // 如果可重试次数小于或等于0,则终止重试 if (--numRetries <= 0) throw; // 如果填写了 exceptionTypes 且异常类型不在 exceptionTypes 之内,则终止重试 if (exceptionTypes != null && exceptionTypes.Length > 0 && !exceptionTypes.Any(u => u.IsAssignableFrom(ex.GetType()))) throw; // 如果可重试异常数大于 0,则间隔指定时间后继续执行 if (retryTimeout > 0) Thread.Sleep(retryTimeout); } } } /// /// 获取错误码消息 /// /// /// /// private static string GetErrorCodeMessage(object errorCode, params object[] args) { errorCode = HandleEnumErrorCode(errorCode); // 获取出错的方法 var methodIfException = GetEndPointExceptionMethod(); // 获取异常特性 var ifExceptionAttribute = methodIfException.IfExceptionAttributes.FirstOrDefault(u => HandleEnumErrorCode(u.ErrorCode).ToString().Equals(errorCode.ToString())); // 获取错误码消息 var errorCodeMessage = ifExceptionAttribute == null || string.IsNullOrWhiteSpace(ifExceptionAttribute.ErrorMessage) ? (ErrorCodeMessages.GetValueOrDefault(errorCode.ToString()) ?? _friendlyExceptionSettings.DefaultErrorMessage) : ifExceptionAttribute.ErrorMessage; // 字符串格式化 return MontageErrorMessage(errorCodeMessage, errorCode.ToString() , args != null && args.Length > 0 ? args : ifExceptionAttribute?.Args); } /// /// 处理枚举类型错误码 /// /// 错误码 /// private static object HandleEnumErrorCode(object errorCode) { // 获取类型 var errorType = errorCode.GetType(); // 判断是否是内置枚举类型,如果是解析特性 if (ErrorCodeTypes.Any(u => u == errorType)) { var fieldinfo = errorType.GetField(Enum.GetName(errorType, errorCode)); if (fieldinfo.IsDefined(typeof(ErrorCodeItemMetadataAttribute), true)) { errorCode = GetErrorCodeItemMessage(fieldinfo).Key; } } return errorCode; } /// /// 获取错误代码类型 /// /// private static IEnumerable GetErrorCodeTypes() { // 查找所有公开的枚举贴有 [ErrorCodeType] 特性的类型 var errorCodeTypes = App.EffectiveTypes .Where(u => u.IsDefined(typeof(ErrorCodeTypeAttribute), true) && u.IsEnum); // 获取错误代码提供器中定义的类型 var errorCodeTypeProvider = App.GetService(App.RootServices); if (errorCodeTypeProvider is { Definitions: not null }) errorCodeTypes = errorCodeTypes.Concat(errorCodeTypeProvider.Definitions); return errorCodeTypes.Distinct(); } /// /// 获取所有错误消息 /// /// private static ConcurrentDictionary GetErrorCodeMessages() { var defaultErrorCodeMessages = new ConcurrentDictionary(); // 查找所有 [ErrorCodeType] 类型中的 [ErrorCodeMetadata] 元数据定义 var errorCodeMessages = ErrorCodeTypes.SelectMany(u => u.GetFields().Where(u => u.IsDefined(typeof(ErrorCodeItemMetadataAttribute)))) .Select(u => GetErrorCodeItemMessage(u)) .ToDictionary(u => u.Key.ToString(), u => u.Value); defaultErrorCodeMessages.AddOrUpdate(errorCodeMessages); // 加载配置文件状态码 var errorCodeMessageSettings = App.GetConfig("ErrorCodeMessageSettings", true); if (errorCodeMessageSettings is { Definitions: not null }) { // 获取所有参数大于1的配置 var fitErrorCodes = errorCodeMessageSettings.Definitions .Where(u => u.Length > 1) .ToDictionary(u => u[0].ToString(), u => FixErrorCodeSettingMessage(u)); defaultErrorCodeMessages.AddOrUpdate(fitErrorCodes); } return defaultErrorCodeMessages; } /// /// 处理异常配置数据 /// /// 错误消息配置对象 /// /// 方式:数组第一个元素为错误码,第二个参数为错误消息,剩下的参数为错误码格式化字符串 /// /// private static string FixErrorCodeSettingMessage(object[] errorCodes) { var args = errorCodes.Skip(2).ToArray(); var errorMessage = errorCodes[1].ToString(); return errorMessage.Format(args); } /// /// 获取堆栈中顶部抛异常方法 /// /// private static MethodIfException GetEndPointExceptionMethod() { // 获取调用堆栈信息 var stackTrace = EnhancedStackTrace.Current(); // 获取出错的堆栈信息 var stackFrame = stackTrace.FirstOrDefault(u => typeof(ControllerBase).IsAssignableFrom(u.MethodInfo.DeclaringType) || typeof(IDynamicApiController).IsAssignableFrom(u.MethodInfo.DeclaringType) || u.StackFrame.GetMethod().IsFinal); // 获取出错的方法 var errorMethod = stackFrame.MethodInfo.MethodBase; // 判断是否已经缓存过该方法,避免重复解析 var isCached = ErrorMethods.TryGetValue(errorMethod, out var methodIfException); if (isCached) return methodIfException; // 获取堆栈中所有的 [IfException] 特性 var ifExceptionAttributes = stackTrace .Where(u => u.MethodInfo.MethodBase != null && u.MethodInfo.MethodBase.IsDefined(typeof(IfExceptionAttribute), true)) .SelectMany(u => u.MethodInfo.MethodBase.GetCustomAttributes(true)) .Where(u => u.ErrorCode != null); // 组装方法异常对象 methodIfException = new MethodIfException { ErrorMethod = errorMethod, IfExceptionAttributes = ifExceptionAttributes }; // 存入缓存 ErrorMethods.TryAdd(errorMethod, methodIfException); return methodIfException; } /// /// 获取错误代码消息实体 /// /// 字段对象 /// (object key, object value) private static (object Key, string Value) GetErrorCodeItemMessage(FieldInfo fieldInfo) { var errorCodeItemMetadata = fieldInfo.GetCustomAttribute(); return (errorCodeItemMetadata.ErrorCode ?? fieldInfo.Name, errorCodeItemMetadata.ErrorMessage.Format(errorCodeItemMetadata.Args)); } /// /// 获取错误码字符串 /// /// /// /// /// private static string MontageErrorMessage(string errorMessage, string errorCode, params object[] args) { // 支持读取配置渲染 var realErrorMessage = errorMessage.Render(); // 多语言处理 realErrorMessage = L.Text == null ? realErrorMessage : L.Text[realErrorMessage]; // 判断是否隐藏错误码 var msg = (_friendlyExceptionSettings.HideErrorCode == true || string.IsNullOrWhiteSpace(errorCode) ? string.Empty : $"[{errorCode}] ") + realErrorMessage; return msg.Format(args); } } }