Files
ThingsGateway/src/Admin/ThingsGateway.Furion/FriendlyException/Oops.cs
2025-10-09 19:05:33 +08:00

379 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ------------------------------------------------------------------------
// 版权信息
// 版权归百小僧及百签科技(广东)有限公司所有。
// 所有权利保留。
// 官方网站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;
using ThingsGateway.Extension;
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);
}
}