diff --git a/framework/Furion/FriendlyException/Oops.cs b/framework/Furion/FriendlyException/Oops.cs index ca7662414dfa71c25249798180fbce7377c5fd6f..06b1f8bf77168e9c476afd19502b49aea3dd2d53 100644 --- a/framework/Furion/FriendlyException/Oops.cs +++ b/framework/Furion/FriendlyException/Oops.cs @@ -22,6 +22,7 @@ using System.Collections.Concurrent; using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Reflection; +using System.Security.AccessControl; namespace Furion.FriendlyException; @@ -75,6 +76,20 @@ public static class Oops return friendlyException; } + /// + /// 抛出业务异常信息 + /// + /// 异常消息 + /// String.Format 参数 + /// 多语言资源类 + /// 异常实例 + public static AppFriendlyException Bah(string errorMessage, params object[] args) + { + var friendlyException = OhLocal(errorMessage, typeof(ValidationException), args).StatusCode(StatusCodes.Status400BadRequest); + friendlyException.ValidationException = true; + return friendlyException; + } + /// /// 抛出业务异常信息 /// @@ -88,6 +103,20 @@ public static class Oops return friendlyException; } + /// + /// 抛出业务异常信息 + /// + /// 错误码 + /// String.Format 参数 + /// 多语言资源类 + /// 异常实例 + public static AppFriendlyException Bah(object errorCode, params object[] args) + { + var friendlyException = OhLocal(errorCode, typeof(ValidationException), args).StatusCode(StatusCodes.Status400BadRequest); + friendlyException.ValidationException = true; + return friendlyException; + } + /// /// 抛出字符串异常 /// @@ -106,6 +135,25 @@ public static class Oops } return friendlyException; } + /// + /// 抛出字符串异常 + /// + /// 异常消息 + /// String.Format 参数 + /// 多语言资源类 + /// 异常实例 + public static AppFriendlyException OhLocal(string errorMessage, params object[] args) + { + var friendlyException = new AppFriendlyException(MontageErrorMessage(errorMessage, default, args), default); + + // 处理默认配置为业务异常问题 + if (_friendlyExceptionSettings.ThrowBah == true) + { + friendlyException.StatusCode(StatusCodes.Status400BadRequest); + friendlyException.ValidationException = true; + } + return friendlyException; + } /// /// 抛出字符串异常 @@ -120,6 +168,20 @@ public static class Oops return new AppFriendlyException(exceptionMessage, default, Activator.CreateInstance(exceptionType, new object[] { exceptionMessage }) as Exception); } + /// + /// 抛出字符串异常 + /// + /// 异常消息 + /// 具体异常类型 + /// String.Format 参数 + /// 多语言资源类 + /// 异常实例 + public static AppFriendlyException OhLocal(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); + } /// /// 抛出字符串异常 @@ -133,6 +195,19 @@ public static class Oops { return Oh(errorMessage, typeof(TException), args); } + /// + /// 抛出字符串异常 + /// + /// 具体异常类型 + /// 多语言资源类 + /// 异常消息 + /// String.Format 参数 + /// 异常实例 + public static AppFriendlyException OhLocal(string errorMessage, params object[] args) + where TException : class + { + return OhLocal(errorMessage, typeof(TException), args); + } /// /// 抛出错误码异常 @@ -154,6 +229,28 @@ public static class Oops return friendlyException; } + + /// + /// 抛出错误码异常 + /// + /// 错误码 + /// String.Format 参数 + /// 多语言资源类 + /// 异常实例 + public static AppFriendlyException OhLocal(object errorCode, params object[] args) + { + var (ErrorCode, Message) = GetErrorCodeMessage(errorCode, args); + var friendlyException = new AppFriendlyException(Message, errorCode) { ErrorCode = ErrorCode }; + + // 处理默认配置为业务异常问题 + if (_friendlyExceptionSettings.ThrowBah == true) + { + friendlyException.StatusCode(StatusCodes.Status400BadRequest); + friendlyException.ValidationException = true; + } + return friendlyException; + } + /// /// 抛出错误码异常 /// @@ -169,6 +266,22 @@ public static class Oops { ErrorCode = ErrorCode }; } + /// + /// 抛出错误码异常 + /// + /// 错误码 + /// 具体异常类型 + /// String.Format 参数 + /// 多语言资源类 + /// 异常实例 + public static AppFriendlyException OhLocal(object errorCode, Type exceptionType, params object[] args) + { + var (ErrorCode, Message) = GetErrorCodeMessage(errorCode, args); + return new AppFriendlyException(Message, errorCode, + Activator.CreateInstance(exceptionType, new object[] { Message }) as Exception) + { ErrorCode = ErrorCode }; + } + /// /// 抛出错误码异常 /// @@ -182,6 +295,20 @@ public static class Oops return Oh(errorCode, typeof(TException), args); } + /// + /// 抛出错误码异常 + /// + /// 具体异常类型 + /// 多语言资源类 + /// 错误码 + /// String.Format 参数 + /// 异常实例 + public static AppFriendlyException OhLocal(object errorCode, params object[] args) + where TException : class + { + return OhLocal(errorCode, typeof(TException), args); + } + /// /// 获取错误码消息 /// @@ -213,6 +340,38 @@ public static class Oops return (errorCode, MontageErrorMessage(errorCodeMessage, errorCode.ToString() , args != null && args.Length > 0 ? args : ifExceptionAttribute?.Args)); } + /// + /// 获取错误码消息 + /// + /// + /// + /// 多语言资源类 + /// + private static (object ErrorCode, string Message) GetErrorCodeMessage(object errorCode, params object[] args) + { + errorCode = HandleEnumErrorCode(errorCode); + + // 获取出错的方法 + var methodIfException = GetEndPointExceptionMethod(); + + // 获取当前状态码匹配异常特性 + var ifExceptionAttribute = methodIfException?.IfExceptionAttributes?.FirstOrDefault(u => u.ErrorCode != null && HandleEnumErrorCode(u.ErrorCode).ToString().Equals(errorCode.ToString())); + + // 获取错误码消息 + var errorCodeMessage = ifExceptionAttribute == null || string.IsNullOrWhiteSpace(ifExceptionAttribute.ErrorMessage) + ? (ErrorCodeMessages.GetValueOrDefault(errorCode.ToString()) ?? _friendlyExceptionSettings.DefaultErrorMessage) + : ifExceptionAttribute.ErrorMessage; + + // 如果所有错误码都获取不到,则找全局 [IfException] 错误 + if (string.IsNullOrWhiteSpace(errorCodeMessage)) + { + errorCodeMessage = methodIfException?.IfExceptionAttributes?.FirstOrDefault(u => u.ErrorCode == null && !string.IsNullOrWhiteSpace(u.ErrorMessage))?.ErrorMessage; + } + + // 字符串格式化 + return (errorCode, MontageErrorMessage(errorCodeMessage, errorCode.ToString() + , args != null && args.Length > 0 ? args : ifExceptionAttribute?.Args)); + } /// /// 处理枚举类型错误码 @@ -370,6 +529,29 @@ public static class Oops // 多语言处理 realErrorMessage = L.Text == null ? realErrorMessage : L.Text[realErrorMessage]; + // 判断是否隐藏错误码 + var msg = (_friendlyExceptionSettings.HideErrorCode == true || string.IsNullOrWhiteSpace(errorCode) + ? string.Empty + : $"[{errorCode}] ") + realErrorMessage; + + return msg.Format(args); + } + /// + /// 获取错误码字符串 + /// + /// + /// + /// + /// 多语言资源类 + /// + private static string MontageErrorMessage(string errorMessage, string errorCode, params object[] args) + { + // 支持读取配置渲染 + var realErrorMessage = errorMessage.Render(); + + // 多语言处理 + realErrorMessage = L.TextOf()[realErrorMessage]; + // 判断是否隐藏错误码 var msg = (_friendlyExceptionSettings.HideErrorCode == true || string.IsNullOrWhiteSpace(errorCode) ? string.Empty diff --git a/tests/Furion.TestProject/Furion.TestProject.csproj b/tests/Furion.TestProject/Furion.TestProject.csproj index 767ea40e1a9b383c7f90dcf9157e7b31a9310087..0ab4fe50009699d7a22396d31ca4dda0ae053bb8 100644 --- a/tests/Furion.TestProject/Furion.TestProject.csproj +++ b/tests/Furion.TestProject/Furion.TestProject.csproj @@ -21,6 +21,30 @@ + + + True + True + TestResource.resx + + + True + True + SharedResource.resx + + + + + + PublicResXFileCodeGenerator + TestResource.Designer.cs + + + PublicResXFileCodeGenerator + SharedResource.Designer.cs + + + diff --git a/tests/Furion.TestProject/LocalizationTest.cs b/tests/Furion.TestProject/LocalizationTest.cs new file mode 100644 index 0000000000000000000000000000000000000000..3c762dcff5164deb4b63892b1ac5063acbecf7cf --- /dev/null +++ b/tests/Furion.TestProject/LocalizationTest.cs @@ -0,0 +1,46 @@ +using Furion.DynamicApiController; +using Furion.FriendlyException; +using Furion.TestProject.OtherResources; +using Furion.TestProject.Resources; +using Microsoft.AspNetCore.Mvc; + +namespace Furion.TestProject +{ + /// + /// 多语言测试 + /// + public class LocalizationTest : IDynamicApiController + { + /// + /// 测试 Oops 异常多语言 + /// + /// + [HttpGet] + public string TestOopsLocalization(int type) + { + try + { + switch (type) + { + //默认配置资源 + case 0: throw Oops.Bah("test_error_code"); + //指定默认的资源 + case 1: throw Oops.Bah("test_error_code"); + //指定其他资源 + case 2: throw Oops.Bah("test_error_code"); + //默认配置资源 + case 3: throw Oops.Oh("test_error_code"); + //指定默认的资源 + case 4: throw Oops.OhLocal("test_error_code"); + //指定其他资源 + case 5: throw Oops.OhLocal("test_error_code"); + } + }catch (Exception ex) + { + return ex.Message; + } + + return null; + } + } +} diff --git a/tests/Furion.TestProject/OtherResources/TestResource.Designer.cs b/tests/Furion.TestProject/OtherResources/TestResource.Designer.cs new file mode 100644 index 0000000000000000000000000000000000000000..e729110d4b274a4af5ad51f3bd0304c69d41e6cd --- /dev/null +++ b/tests/Furion.TestProject/OtherResources/TestResource.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Furion.TestProject.OtherResources { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class TestResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal TestResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Furion.TestProject.OtherResources.TestResource", typeof(TestResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 的本地化字符串。 + /// + public static string test_error_code { + get { + return ResourceManager.GetString("test_error_code", resourceCulture); + } + } + } +} diff --git a/tests/Furion.TestProject/OtherResources/TestResource.en.resx b/tests/Furion.TestProject/OtherResources/TestResource.en.resx new file mode 100644 index 0000000000000000000000000000000000000000..41143081266bb320f562220c3ac2188fd2686669 --- /dev/null +++ b/tests/Furion.TestProject/OtherResources/TestResource.en.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + other test error + + \ No newline at end of file diff --git a/tests/Furion.TestProject/OtherResources/TestResource.resx b/tests/Furion.TestProject/OtherResources/TestResource.resx new file mode 100644 index 0000000000000000000000000000000000000000..09dfbfe23603b575fd85b2667d127eab756d90c3 --- /dev/null +++ b/tests/Furion.TestProject/OtherResources/TestResource.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + \ No newline at end of file diff --git a/tests/Furion.TestProject/OtherResources/TestResource.zh.resx b/tests/Furion.TestProject/OtherResources/TestResource.zh.resx new file mode 100644 index 0000000000000000000000000000000000000000..9c6cce62fe5a6ac732d0e6b66260c0d330e4999c --- /dev/null +++ b/tests/Furion.TestProject/OtherResources/TestResource.zh.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Other 测试错误 + + \ No newline at end of file diff --git a/tests/Furion.TestProject/Resources/SharedResource.Designer.cs b/tests/Furion.TestProject/Resources/SharedResource.Designer.cs new file mode 100644 index 0000000000000000000000000000000000000000..068a17b6eadc9d61ef9e39749f0f211c39a5a9fe --- /dev/null +++ b/tests/Furion.TestProject/Resources/SharedResource.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace Furion.TestProject.Resources { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class SharedResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal SharedResource() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Furion.TestProject.Resources.SharedResource", typeof(SharedResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// 查找类似 的本地化字符串。 + /// + public static string test_error_code { + get { + return ResourceManager.GetString("test_error_code", resourceCulture); + } + } + } +} diff --git a/tests/Furion.TestProject/Resources/SharedResource.en.resx b/tests/Furion.TestProject/Resources/SharedResource.en.resx new file mode 100644 index 0000000000000000000000000000000000000000..e6a4921405f836e908e5eea0627472270ba0ff98 --- /dev/null +++ b/tests/Furion.TestProject/Resources/SharedResource.en.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + test error + + \ No newline at end of file diff --git a/tests/Furion.TestProject/Resources/SharedResource.resx b/tests/Furion.TestProject/Resources/SharedResource.resx new file mode 100644 index 0000000000000000000000000000000000000000..09dfbfe23603b575fd85b2667d127eab756d90c3 --- /dev/null +++ b/tests/Furion.TestProject/Resources/SharedResource.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + \ No newline at end of file diff --git a/tests/Furion.TestProject/Resources/SharedResource.zh.resx b/tests/Furion.TestProject/Resources/SharedResource.zh.resx new file mode 100644 index 0000000000000000000000000000000000000000..71d13d8fed17eb7e1b1637f2ef930ac167f51a72 --- /dev/null +++ b/tests/Furion.TestProject/Resources/SharedResource.zh.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 测试错误 + + \ No newline at end of file diff --git a/tests/Furion.TestProject/Startup.cs b/tests/Furion.TestProject/Startup.cs index 603d69598414b2c0e409c04d68dcd51ffb1689c9..3334c54a822d1352eb3ab41121d07d9ab3bb463b 100644 --- a/tests/Furion.TestProject/Startup.cs +++ b/tests/Furion.TestProject/Startup.cs @@ -4,7 +4,9 @@ public class Startup : AppStartup { public void ConfigureServices(IServiceCollection services) { - services.AddControllers().AddInject(); + services.AddControllers() + .AddInject() + .AddAppLocalization(); services.AddViewEngine(); } @@ -14,6 +16,8 @@ public class Startup : AppStartup { app.UseDeveloperExceptionPage(); } + + app.UseAppLocalization(); app.UseHttpsRedirection(); diff --git a/tests/Furion.TestProject/appsettings.json b/tests/Furion.TestProject/appsettings.json index 546ca941db2f128cb26cbe2e90c6f87349bc30e8..8ce1a177f0d48023c927bd3076c85f03ca8ecb6d 100644 --- a/tests/Furion.TestProject/appsettings.json +++ b/tests/Furion.TestProject/appsettings.json @@ -35,5 +35,12 @@ } } ] + }, + "LocalizationSettings": { + "SupportedCultures": [ "zh-CN", "en-US" ], + "DefaultCulture": "zh-CN", + "LanguageFilePrefix": "Furion.TestProject.Resources.SharedResource", + "AssemblyName": "Furion.TestProject", + "ResourcesPath": "" } } \ No newline at end of file