Files
ThingsGateway/src/Admin/ThingsGateway.Furion/FriendlyException/Oops.cs

379 lines
14 KiB
C#
Raw Normal View History

2025-01-24 22:42:26 +08:00
// ------------------------------------------------------------------------
// 版权信息
// 版权归百小僧及百签科技(广东)有限公司所有。
// 所有权利保留。
// 官方网站https://baiqian.com
//
// 许可证信息
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// ------------------------------------------------------------------------
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Reflection;
2025-10-09 19:05:33 +08:00
using ThingsGateway.Extension;
2025-01-24 22:42:26 +08:00
using ThingsGateway.Templates.Extensions;
namespace ThingsGateway.FriendlyException;
/// <summary>
/// 抛异常静态类
/// </summary>
[SuppressSniffer]
public static class Oops
{
/// <summary>
/// 方法错误异常特性
/// </summary>
private static readonly ConcurrentDictionary<MethodBase, MethodIfException> _errorMethods;
/// <summary>
/// 错误代码类型
/// </summary>
private static readonly IEnumerable<Type> _errorCodeTypes;
/// <summary>
/// 错误消息字典
/// </summary>
private static readonly ConcurrentDictionary<string, string> _errorCodeMessages;
/// <summary>
/// 友好异常设置
/// </summary>
private static readonly FriendlyExceptionSettingsOptions _friendlyExceptionSettings;
/// <summary>
/// 构造函数
/// </summary>
static Oops()
{
_errorMethods = new ConcurrentDictionary<MethodBase, MethodIfException>();
_friendlyExceptionSettings = App.GetConfig<FriendlyExceptionSettingsOptions>("FriendlyExceptionSettings", true);
_errorCodeTypes = GetErrorCodeTypes();
_errorCodeMessages = GetErrorCodeMessages();
}
/// <summary>
/// 抛出业务异常信息
/// </summary>
/// <param name="errorMessage">异常消息</param>
/// <param name="args">String.Format 参数</param>
/// <returns>异常实例</returns>
public static AppFriendlyException Bah(string errorMessage, params object[] args)
{
var friendlyException = Oh(errorMessage, typeof(ValidationException), args).StatusCode(StatusCodes.Status400BadRequest);
friendlyException.ValidationException = true;
return friendlyException;
}
/// <summary>
/// 抛出业务异常信息
/// </summary>
/// <param name="errorCode">错误码</param>
/// <param name="args">String.Format 参数</param>
/// <returns>异常实例</returns>
public static AppFriendlyException Bah(object errorCode, params object[] args)
{
var friendlyException = Oh(errorCode, typeof(ValidationException), args).StatusCode(StatusCodes.Status400BadRequest);
friendlyException.ValidationException = true;
return friendlyException;
}
/// <summary>
/// 抛出字符串异常
/// </summary>
/// <param name="errorMessage">异常消息</param>
/// <param name="args">String.Format 参数</param>
/// <returns>异常实例</returns>
public static AppFriendlyException Oh(string errorMessage, params object[] args)
{
var friendlyException = new AppFriendlyException(MontageErrorMessage(errorMessage, default, null, args), default);
// 处理默认配置为业务异常问题
if (_friendlyExceptionSettings.ThrowBah == true)
{
friendlyException.StatusCode(StatusCodes.Status400BadRequest);
friendlyException.ValidationException = true;
}
return friendlyException;
}
/// <summary>
/// 抛出字符串异常
/// </summary>
/// <param name="errorMessage">异常消息</param>
/// <param name="exceptionType">具体异常类型</param>
/// <param name="args">String.Format 参数</param>
/// <returns>异常实例</returns>
public static AppFriendlyException Oh(string errorMessage, Type exceptionType, params object[] args)
{
var exceptionMessage = MontageErrorMessage(errorMessage, default, null, args);
return new AppFriendlyException(exceptionMessage, default,
Activator.CreateInstance(exceptionType, new object[] { exceptionMessage }) as Exception);
}
/// <summary>
/// 抛出字符串异常
/// </summary>
/// <typeparam name="TException">具体异常类型</typeparam>
/// <param name="errorMessage">异常消息</param>
/// <param name="args">String.Format 参数</param>
/// <returns>异常实例</returns>
public static AppFriendlyException Oh<TException>(string errorMessage, params object[] args)
where TException : class
{
return Oh(errorMessage, typeof(TException), args);
}
/// <summary>
/// 抛出错误码异常
/// </summary>
/// <param name="errorCode">错误码</param>
/// <param name="args">String.Format 参数</param>
/// <returns>异常实例</returns>
public static AppFriendlyException Oh(object errorCode, params object[] args)
{
var (ErrorCode, Message) = GetErrorCodeMessage(errorCode, null, args);
var friendlyException = new AppFriendlyException(Message, errorCode) { ErrorCode = ErrorCode };
// 处理默认配置为业务异常问题
if (_friendlyExceptionSettings.ThrowBah == true)
{
friendlyException.StatusCode(StatusCodes.Status400BadRequest);
friendlyException.ValidationException = true;
}
return friendlyException;
}
/// <summary>
/// 抛出错误码异常
/// </summary>
/// <param name="errorCode">错误码</param>
/// <param name="exceptionType">具体异常类型</param>
/// <param name="args">String.Format 参数</param>
/// <returns>异常实例</returns>
public static AppFriendlyException Oh(object errorCode, Type exceptionType, params object[] args)
{
var (ErrorCode, Message) = GetErrorCodeMessage(errorCode, null, args);
return new AppFriendlyException(Message, errorCode,
Activator.CreateInstance(exceptionType, new object[] { Message }) as Exception)
{ ErrorCode = ErrorCode };
}
/// <summary>
/// 抛出错误码异常
/// </summary>
/// <typeparam name="TException">具体异常类型</typeparam>
/// <param name="errorCode">错误码</param>
/// <param name="args">String.Format 参数</param>
/// <returns>异常实例</returns>
public static AppFriendlyException Oh<TException>(object errorCode, params object[] args)
where TException : class
{
return Oh(errorCode, typeof(TException), args);
}
/// <summary>
/// 获取错误码错误消息
/// </summary>
/// <param name="errorCode"></param>
/// <param name="hideErrorCode"></param>
/// <param name="args"></param>
/// <returns></returns>
public static string Text(object errorCode, bool? hideErrorCode = null, params object[] args)
{
var (_, Message) = GetErrorCodeMessage(errorCode, hideErrorCode, args);
return Message;
}
/// <summary>
/// 获取错误码消息
/// </summary>
/// <param name="errorCode"></param>
/// <param name="hideErrorCode"></param>
/// <param name="args"></param>
/// <returns></returns>
private static (object ErrorCode, string Message) GetErrorCodeMessage(object errorCode, bool? hideErrorCode, params object[] args)
{
errorCode = HandleEnumErrorCode(errorCode);
// 获取出错的方法
var methodIfException = GetEndPointExceptionMethod();
// 获取错误码消息
var errorCodeMessage = _errorCodeMessages.GetValueOrDefault(errorCode.ToString()) ?? _friendlyExceptionSettings.DefaultErrorMessage;
// 字符串格式化
return (errorCode, MontageErrorMessage(errorCodeMessage, errorCode.ToString(), hideErrorCode
, args));
}
/// <summary>
/// 处理枚举类型错误码
/// </summary>
/// <param name="errorCode">错误码</param>
/// <returns></returns>
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 = GetErrorCodeItemInformation(fieldinfo).Key;
}
}
return errorCode;
}
/// <summary>
/// 获取错误代码类型
/// </summary>
/// <returns></returns>
private static IEnumerable<Type> GetErrorCodeTypes()
{
// 查找所有公开的枚举贴有 [ErrorCodeType] 特性的类型
var errorCodeTypes = App.EffectiveTypes
.Where(u => u.IsDefined(typeof(ErrorCodeTypeAttribute), true) && u.IsEnum);
// 获取错误代码提供器中定义的类型
var errorCodeTypeProvider = App.GetService<IErrorCodeTypeProvider>(App.RootServices);
if (errorCodeTypeProvider is { Definitions: not null }) errorCodeTypes = errorCodeTypes.Concat(errorCodeTypeProvider.Definitions);
return errorCodeTypes.Distinct();
}
/// <summary>
/// 获取所有错误消息
/// </summary>
/// <returns></returns>
private static ConcurrentDictionary<string, string> GetErrorCodeMessages()
{
var defaultErrorCodeMessages = new ConcurrentDictionary<string, string>();
// 查找所有 [ErrorCodeType] 类型中的 [ErrorCodeMetadata] 元数据定义
var errorCodeMessages = _errorCodeTypes.SelectMany(u => u.GetFields().Where(u => u.IsDefined(typeof(ErrorCodeItemMetadataAttribute))))
.Select(u => GetErrorCodeItemInformation(u))
.ToDictionary(u => u.Key.ToString(), u => u.Value);
defaultErrorCodeMessages.AddOrUpdate(errorCodeMessages);
// 加载配置文件状态码
var errorCodeMessageSettings = App.GetConfig<ErrorCodeMessageSettingsOptions>("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;
}
/// <summary>
/// 处理异常配置数据
/// </summary>
/// <param name="errorCodes">错误消息配置对象</param>
/// <remarks>
/// 方式:数组第一个元素为错误码,第二个参数为错误消息,剩下的参数为错误码格式化字符串
/// </remarks>
/// <returns></returns>
private static string FixErrorCodeSettingMessage(object[] errorCodes)
{
var args = errorCodes.Skip(2).ToArray();
var errorMessage = errorCodes[1].ToString();
return errorMessage.Format(args);
}
/// <summary>
/// 获取堆栈中顶部抛异常方法
/// </summary>
/// <returns></returns>
private static MethodIfException GetEndPointExceptionMethod()
{
try
{
// 获取调用堆栈信息
var stackTrace = EnhancedStackTrace.Current();
// 获取出错的堆栈信息,在 web 请求中获取控制器或动态API的方法除外获取第一个出错的方法
var stackFrame = stackTrace.FirstOrDefault(u =>
typeof(ControllerBase).IsAssignableFrom(u.MethodInfo.DeclaringType)
//|| typeof(IDynamicApiController).IsAssignableFrom(u.MethodInfo.DeclaringType)
)
?? stackTrace.FirstOrDefault(u => u.GetMethod().DeclaringType.Namespace != typeof(Oops).Namespace);
// 获取出错的方法
var errorMethod = stackFrame.MethodInfo.MethodBase;
// 判断是否已经缓存过该方法,避免重复解析
var isCached = _errorMethods.TryGetValue(errorMethod, out var methodIfException);
if (isCached) return methodIfException;
// 组装方法异常对象
methodIfException = new MethodIfException
{
ErrorMethod = errorMethod,
};
// 存入缓存
_errorMethods.TryAdd(errorMethod, methodIfException);
return methodIfException;
}
catch
{
return null;
}
}
/// <summary>
/// 获取错误代码信息
/// </summary>
/// <param name="fieldInfo">字段对象</param>
/// <returns>(object key, object value)</returns>
private static (object Key, string Value) GetErrorCodeItemInformation(FieldInfo fieldInfo)
{
var errorCodeItemMetadata = fieldInfo.GetCustomAttribute<ErrorCodeItemMetadataAttribute>();
return (errorCodeItemMetadata.ErrorCode ?? fieldInfo.Name, errorCodeItemMetadata.ErrorMessage.Format(errorCodeItemMetadata.Args));
}
/// <summary>
/// 获取错误码字符串
/// </summary>
/// <param name="errorMessage"></param>
/// <param name="errorCode"></param>
/// <param name="hideErrorCode">隐藏错误码</param>
/// <param name="args"></param>
/// <returns></returns>
private static string MontageErrorMessage(string errorMessage, string errorCode, bool? hideErrorCode, params object[] args)
{
// 支持读取配置渲染
var realErrorMessage = errorMessage.Render();
// 多语言处理
realErrorMessage = App.StringLocalizerFactory == null ? realErrorMessage : App.CreateLocalizerByType(typeof(Oops))[realErrorMessage];
// 判断是否隐藏错误码
var msg = (hideErrorCode == true || _friendlyExceptionSettings.HideErrorCode == true || string.IsNullOrWhiteSpace(errorCode)
? string.Empty
: $"[{errorCode}] ") + realErrorMessage;
return msg.Format(args);
}
}