diff --git a/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs b/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs
index 6367bd24ff..eb8c4eb804 100644
--- a/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs
+++ b/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs
@@ -62,12 +62,6 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling
return CreateEntityNotFoundError(exception as EntityNotFoundException);
}
- if (exception is AbpAuthorizationException)
- {
- var authorizationException = exception as AbpAuthorizationException;
- return new RemoteServiceErrorInfo(authorizationException.Message);
- }
-
var errorInfo = new RemoteServiceErrorInfo();
if (exception is IUserFriendlyException)
diff --git a/framework/src/Volo.Abp.Features/Volo.Abp.Features.csproj b/framework/src/Volo.Abp.Features/Volo.Abp.Features.csproj
index a2d098f970..3bea3ea77f 100644
--- a/framework/src/Volo.Abp.Features/Volo.Abp.Features.csproj
+++ b/framework/src/Volo.Abp.Features/Volo.Abp.Features.csproj
@@ -15,7 +15,12 @@
-
+
+
+
+
+
+
diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeatureErrorCodes.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeatureErrorCodes.cs
new file mode 100644
index 0000000000..a5a4cf3a58
--- /dev/null
+++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeatureErrorCodes.cs
@@ -0,0 +1,11 @@
+namespace Volo.Abp.Features
+{
+ public static class AbpFeatureErrorCodes
+ {
+ public const string FeatureIsNotEnabled = "Volo.Feature:010001";
+
+ public const string AllOfTheseFeaturesMustBeEnabled = "Volo.Feature:010002";
+
+ public const string AtLeastOneOfTheseFeaturesMustBeEnabled = "Volo.Feature:010003";
+ }
+}
diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs
index f30017feed..70c6e7f37d 100644
--- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs
+++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs
@@ -1,15 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
+using Volo.Abp.Features.Localization;
using Volo.Abp.Localization;
+using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Validation;
+using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Features
{
[DependsOn(
- typeof(AbpLocalizationAbstractionsModule),
+ typeof(AbpLocalizationModule),
typeof(AbpMultiTenancyModule),
typeof(AbpValidationModule)
)]
@@ -29,6 +32,23 @@ namespace Volo.Abp.Features
options.ValueProviders.Add();
options.ValueProviders.Add();
});
+
+ Configure(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ options.Resources
+ .Add("en")
+ .AddVirtualJson("/Volo/Abp/Features/Localization");
+ });
+
+ Configure(options =>
+ {
+ options.MapCodeNamespace("Volo.Feature", typeof(AbpFeatureResource));
+ });
}
private static void AutoAddDefinitionProviders(IServiceCollection services)
diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerExtensions.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerExtensions.cs
index 8850408df0..ba2b640031 100644
--- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerExtensions.cs
+++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerExtensions.cs
@@ -9,8 +9,8 @@ namespace Volo.Abp.Features
public static class FeatureCheckerExtensions
{
public static async Task GetAsync(
- [NotNull] this IFeatureChecker featureChecker,
- [NotNull] string name,
+ [NotNull] this IFeatureChecker featureChecker,
+ [NotNull] string name,
T defaultValue = default)
where T : struct
{
@@ -56,10 +56,11 @@ namespace Volo.Abp.Features
{
if (!(await featureChecker.IsEnabledAsync(featureName)))
{
- throw new AbpAuthorizationException("Feature is not enabled: " + featureName);
+ throw new AbpAuthorizationException(code: AbpFeatureErrorCodes.FeatureIsNotEnabled).WithData(
+ "FeatureName", featureName);
}
}
-
+
public static async Task CheckEnabledAsync(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames)
{
if (featureNames.IsNullOrEmpty())
@@ -73,10 +74,8 @@ namespace Volo.Abp.Features
{
if (!(await featureChecker.IsEnabledAsync(featureName)))
{
- throw new AbpAuthorizationException(
- "Required features are not enabled. All of these features must be enabled: " +
- string.Join(", ", featureNames)
- );
+ throw new AbpAuthorizationException(code: AbpFeatureErrorCodes.AllOfTheseFeaturesMustBeEnabled)
+ .WithData("FeatureNames", string.Join(", ", featureNames));
}
}
}
@@ -90,11 +89,9 @@ namespace Volo.Abp.Features
}
}
- throw new AbpAuthorizationException(
- "Required features are not enabled. At least one of these features must be enabled: " +
- string.Join(", ", featureNames)
- );
+ throw new AbpAuthorizationException(code: AbpFeatureErrorCodes.AtLeastOneOfTheseFeaturesMustBeEnabled)
+ .WithData("FeatureNames", string.Join(", ", featureNames));
}
}
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/AbpFeatureResource.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/AbpFeatureResource.cs
new file mode 100644
index 0000000000..5871d550ad
--- /dev/null
+++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/AbpFeatureResource.cs
@@ -0,0 +1,10 @@
+using Volo.Abp.Localization;
+
+namespace Volo.Abp.Features.Localization
+{
+ [LocalizationResourceName("AbpFeature")]
+ public class AbpFeatureResource
+ {
+
+ }
+}
diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/en.json b/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/en.json
new file mode 100644
index 0000000000..de03dc11d0
--- /dev/null
+++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/en.json
@@ -0,0 +1,8 @@
+{
+ "culture": "en",
+ "texts": {
+ "Volo.Feature:010001": "Feature is not enabled: {FeatureName}",
+ "Volo.Feature:010002": "Required features are not enabled. All of these features must be enabled: {FeatureNames}",
+ "Volo.Feature:010003": "Required features are not enabled. At least one of these features must be enabled: {FeatureNames}"
+ }
+}
diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/tr.json b/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/tr.json
new file mode 100644
index 0000000000..9277b63530
--- /dev/null
+++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/tr.json
@@ -0,0 +1,8 @@
+{
+ "culture": "tr",
+ "texts": {
+ "Volo.Feature:010001": "Özellik etkinleştirilmedi: {FeatureName}",
+ "Volo.Feature:010002": "Gerekli özellikler etkinleştirilmedi. Bu özelliklerin tümü etkinleştirilmelidir: {FeatureNames}",
+ "Volo.Feature:010003": "Gerekli özellikler etkinleştirilmedi. Bu özelliklerden en az birinin etkinleştirilmesi gerekir: {FeatureNames}"
+ }
+}
diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/zh-Hans.json b/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/zh-Hans.json
new file mode 100644
index 0000000000..4c6d99c281
--- /dev/null
+++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/Localization/zh-Hans.json
@@ -0,0 +1,8 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "Volo.Feature:010001": "功能未启用: {FeatureName}",
+ "Volo.Feature:010002": "必要的功能未启用. 这些功能需要启用: {FeatureNames}",
+ "Volo.Feature:010003": "必要的功能未启用. 需要启用这些功能中的一项:{FeatureNames}"
+ }
+}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Authorization/AbpAuthorizationException.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Authorization/AbpAuthorizationException.cs
index ad5b0b65a3..c0a8c4d4f8 100644
--- a/framework/src/Volo.Abp.Security/Volo/Abp/Authorization/AbpAuthorizationException.cs
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/Authorization/AbpAuthorizationException.cs
@@ -1,6 +1,7 @@
using System;
using System.Runtime.Serialization;
using Microsoft.Extensions.Logging;
+using Volo.Abp.ExceptionHandling;
using Volo.Abp.Logging;
namespace Volo.Abp.Authorization
@@ -9,7 +10,7 @@ namespace Volo.Abp.Authorization
/// This exception is thrown on an unauthorized request.
///
[Serializable]
- public class AbpAuthorizationException : AbpException, IHasLogLevel
+ public class AbpAuthorizationException : AbpException, IHasLogLevel, IHasErrorCode
{
///
/// Severity of the exception.
@@ -17,6 +18,11 @@ namespace Volo.Abp.Authorization
///
public LogLevel LogLevel { get; set; }
+ ///
+ /// Error code.
+ ///
+ public string Code { get; }
+
///
/// Creates a new object.
///
@@ -54,5 +60,24 @@ namespace Volo.Abp.Authorization
{
LogLevel = LogLevel.Warning;
}
+
+ ///
+ /// Creates a new object.
+ ///
+ /// Exception message
+ /// Exception code
+ /// Inner exception
+ public AbpAuthorizationException(string message = null, string code = null, Exception innerException = null)
+ : base(message, innerException)
+ {
+ Code = code;
+ LogLevel = LogLevel.Warning;
+ }
+
+ public AbpAuthorizationException WithData(string name, object value)
+ {
+ Data[name] = value;
+ return this;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj b/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj
index 3edc355ca2..713b541431 100644
--- a/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj
+++ b/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj
@@ -9,6 +9,7 @@
+
diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/AbpFeaturesTestModule.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/AbpFeaturesTestModule.cs
index 7b0927b80d..f5ff20e6f2 100644
--- a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/AbpFeaturesTestModule.cs
+++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/AbpFeaturesTestModule.cs
@@ -1,10 +1,12 @@
using Volo.Abp.Autofac;
+using Volo.Abp.ExceptionHandling;
using Volo.Abp.Modularity;
namespace Volo.Abp.Features
{
[DependsOn(
typeof(AbpFeaturesModule),
+ typeof(AbpExceptionHandlingModule),
typeof(AbpTestBaseModule),
typeof(AbpAutofacModule)
)]
@@ -12,7 +14,7 @@ namespace Volo.Abp.Features
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
-
+
}
}
}
diff --git a/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureCheckerExtensions_Tests.cs b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureCheckerExtensions_Tests.cs
new file mode 100644
index 0000000000..a3ea2cc710
--- /dev/null
+++ b/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureCheckerExtensions_Tests.cs
@@ -0,0 +1,60 @@
+using System.Threading.Tasks;
+using Shouldly;
+using Volo.Abp.AspNetCore.ExceptionHandling;
+using Volo.Abp.Authorization;
+using Volo.Abp.Localization;
+using Xunit;
+
+namespace Volo.Abp.Features
+{
+ public class FeatureCheckerExtensions_Tests : FeatureTestBase
+ {
+ private readonly IFeatureChecker _featureChecker;
+ private readonly IExceptionToErrorInfoConverter _exceptionToErrorInfoConverter;
+
+ public FeatureCheckerExtensions_Tests()
+ {
+ _featureChecker = GetRequiredService();
+ _exceptionToErrorInfoConverter = GetRequiredService();
+ }
+
+ [Fact]
+ public async Task CheckEnabledAsync()
+ {
+ using (CultureHelper.Use("zh-Hans"))
+ {
+ var ex = await Assert.ThrowsAsync(async () =>
+ await _featureChecker.CheckEnabledAsync("BooleanTestFeature1"));
+
+ var errorInfo = _exceptionToErrorInfoConverter.Convert(ex, false);
+ errorInfo.Message.ShouldBe("功能未启用: BooleanTestFeature1");
+ }
+ }
+
+ [Fact]
+ public async Task CheckEnabled_RequiresAll()
+ {
+ using (CultureHelper.Use("zh-Hans"))
+ {
+ var ex = await Assert.ThrowsAsync(async () =>
+ await _featureChecker.CheckEnabledAsync(true, "BooleanTestFeature1", "BooleanTestFeature2"));
+
+ var errorInfo = _exceptionToErrorInfoConverter.Convert(ex, false);
+ errorInfo.Message.ShouldBe("必要的功能未启用. 这些功能需要启用: BooleanTestFeature1, BooleanTestFeature2");
+ }
+ }
+
+ [Fact]
+ public async Task CheckEnabled_Not_RequiresAll()
+ {
+ using (CultureHelper.Use("zh-Hans"))
+ {
+ var ex = await Assert.ThrowsAsync(async () =>
+ await _featureChecker.CheckEnabledAsync(false, "BooleanTestFeature1", "BooleanTestFeature2"));
+
+ var errorInfo = _exceptionToErrorInfoConverter.Convert(ex, false);
+ errorInfo.Message.ShouldBe("必要的功能未启用. 需要启用这些功能中的一项:BooleanTestFeature1, BooleanTestFeature2");
+ }
+ }
+ }
+}