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