- 简介
- 检查特征
- RequiresFeature属性
- 关于拦截
- IFeatureChecker服务
- IsEnabledAsync
- GetOrNullAsync
- 扩展方法
- 定义特征
- FeatureDefinitionProvider
- 其他特征属性
- 特征管理模式
- 子特征
- 更改从属模块的特征定义
- 在客户端检查特征
- ASP.NET Core MVC / Razor页面用户界面
- Angular UI
- 特征管理
- 高级主题
- 特征值提供程序
- 特征仓库
ABP特征系统用于在运行时启用、禁用或更改应用程序特征的行为。
特征的运行时值通常是一个boolean
值,例如true
(启用)或false
(禁用)。但是,您可以获取/设置任何类型的特征值。
特征系统最初旨在控制多租户应用程序中的租户特征。但是,它是可扩展的,并且能够在任何条件下确定特征。
特征系统是通过Volo.Abp.Features NuGet包实现的。大多数情况下,您不需要手动安装它,因为它已随应用程序启动模板一起预安装。
注意:此文基于版本3.1时的文档进行翻译的
检查特征在解释定义特征之前,让我们看一下如何在应用程序代码中检查特征值。
RequiresFeature属性[RequiresFeature]
属性(在Volo.Abp.Features
名称空间中定义)用于声明性地检查特征是否true
(启用)。这是这些boolean
特征的有用快捷方式。
示例:检查是否启用了“PDF报告”特征
public class ReportingAppService : ApplicationService, IReportingAppService
{
[RequiresFeature("MyApp.PdfReporting")]
public async Task GetPdfReportAsync()
{
//TODO...
}
}
-
RequiresFeature(...)
只需获取特征名称即可检查它是否已启用。如果未启用,则将抛出授权异常,并将适当的响应返回给客户端。 -
[RequiresFeature]
可以用于方法或类。当您将它用于一个类时,该类的所有方法都需要给定的特征。 -
RequiresFeature
可能会获得多个特征名称,例如[RequiresFeature("Feature1", "Feature2")]
。在这种情况下,ABP检查是否启用了任何特征。使用RequiresAll
选项,例如[RequiresFeature("Feature1", "Feature2", RequiresAll = true)]
强制检查要启用的所有特征。 -
方法或类支持多次使用
[RequiresFeature]
属性。在这种情况下,ABP check会检查所有它们。
特征名称可以是任意字符串。对于某个特征,它应该是唯一的。
关于拦截ABP框架使用拦截系统使[RequiresFeature]
属性正常工作。因此,它可以与从依赖注入注入的任何类(应用程序服务,控制器…)一起使用。
但是,为了使它起作用,应该遵循一些规则。
-
如果您不是通过接口(如
IMyService
)注入服务,则该服务的方法必须为virtual
。否则,动态代理/拦截系统将无法工作。 -
仅拦截
async
方法(返回一个Task
或Task
的方法)。
控制器和razor页面方法有一个例外。它们不需要上述规则,因为在这种情况下,ABP Framework使用动作/页面过滤器来实现特征检查。
IFeatureChecker服务IFeatureChecker
允许检查您的应用程序代码中的特征。
如果启用给定特征则返回true
。因此,您可以有条件地执行业务流程。
示例:检查是否启用了“PDF报告”特征
public class ReportingAppService : ApplicationService, IReportingAppService
{
private readonly IFeatureChecker _featureChecker;
public ReportingAppService(IFeatureChecker featureChecker)
{
_featureChecker = featureChecker;
}
public async Task GetPdfReportAsync()
{
if (await _featureChecker.IsEnabledAsync("MyApp.PdfReporting"))
{
//TODO...
}
else
{
//TODO...
}
}
}
IsEnabledAsync
通过重载在一个方法调用中检查多个特征。
获取特征的当前值。此方法返回string
,因此你可以通过转换string
来存储任何类型的值。
示例:检查允许的最大产品数量
public class ProductController : AbpController
{
private readonly IFeatureChecker _featureChecker;
public ProductController(IFeatureChecker featureChecker)
{
_featureChecker = featureChecker;
}
public async Task Create(CreateProductModel model)
{
var currentProductCount = await GetCurrentProductCountFromDatabase();
//GET THE FEATURE VALUE
var maxProductCountLimit =
await _featureChecker.GetOrNullAsync("MyApp.MaxProductCount");
if (currentProductCount >= Convert.ToInt32(maxProductCountLimit))
{
throw new BusinessException(
"MyApp:ReachToMaxProductCountLimit",
$"You can not create more than {maxProductCountLimit} products!"
);
}
//TODO: Create the product in the database...
}
private async Task GetCurrentProductCountFromDatabase()
{
throw new System.NotImplementedException();
}
}
本示例使用数字值作为SaaS应用程序中用户/租户的特征限制产品计数。
您可以使用GetAsync
方法的一般重载,而不是将值手动转换为int
:
var maxProductCountLimit = await _featureChecker.GetAsync("MyApp.MaxProductCount");
扩展方法
IFeatureChecker
接口有一些有用的扩展方法。
-
Task GetAsync(string name, T defaultValue = default):
用于获取具有给定类型T
的特征值。允许指定defaultValue
特征值为null
时返回的值。 -
CheckEnabledAsync(string name):
检查是否启用了给定特征。如果特征不为true
(启用),则抛出AbpAuthorizationException
。
应该定义一个特征以能够对其进行检查。
FeatureDefinitionProvider创建一个继承FeatureDefinitionProvider
来定义权限的类。
示例:定义权限
using Volo.Abp.Features;
namespace FeaturesDemo
{
public class MyFeatureDefinitionProvider : FeatureDefinitionProvider
{
public override void Define(IFeatureDefinitionContext context)
{
var myGroup = context.AddGroup("MyApp");
myGroup.AddFeature("MyApp.PdfReporting", defaultValue: "false");
myGroup.AddFeature("MyApp.MaxProductCount", defaultValue: "10");
}
}
}
ABP自动发现此类并注册特征。无需其他配置。
此类通常在Application.Contracts
解决方案的项目中创建。
-
在
Define
方法中,您首先需要为您的应用程序/模块添加特征组或获取现有的特征组,然后将特征添加至该组。 -
第一个特征,命名为
MyApp.PdfReporting
,是一个boolean
特征以false作为默认值。 -
第二个特征名为
MyApp.MaxProductCount
,是数字特征,默认值为10
。
如果没有为当前用户/租户设置其他值,则使用默认值。
其他特征属性这些最低限度的定义足以使特征系统正常运行,但是您可以为特征指定可选属性;
-
DisplayName
:可本地化的字符串,将用于在用户界面上显示特征名称。 -
Description
:较长的可本地化文字,用于描述特征。 -
ValueType
:特征值的类型。可以是实现IStringValueType
的类。内置类型:-
ToggleStringValueType
:用于定义true
/false
,on
/off
,enabled
/disabled
样式特征。UI上显示一个复选框。 -
FreeTextStringValueType
:用于定义自由文本值。UI上显示一个文本框。 -
SelectionStringValueType
:用于强制从列表中选择值。UI上显示一个下拉列表。
-
-
IsVisibleToClients
(默认值:true
):设置为false
可向客户端(浏览器)隐藏此特征的值。与客户共享值可帮助他们根据特征值有条件地显示/隐藏/更改UI部件。 -
Properties
:字典,用于设置/获取与此特征相关的任意键值对。这可能是定制的重点。
因此,根据这些描述,最好定义这些特征,如下所示:
using FeaturesDemo.Localization;
using Volo.Abp.Features;
using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues;
namespace FeaturesDemo
{
public class MyFeatureDefinitionProvider : FeatureDefinitionProvider
{
public override void Define(IFeatureDefinitionContext context)
{
var myGroup = context.AddGroup("MyApp");
myGroup.AddFeature(
"MyApp.PdfReporting",
defaultValue: "false",
displayName: LocalizableString
.Create("PdfReporting"),
valueType: new ToggleStringValueType()
);
myGroup.AddFeature(
"MyApp.MaxProductCount",
defaultValue: "10",
displayName: LocalizableString
.Create("MaxProductCount"),
valueType: new FreeTextStringValueType(
new NumericValueValidator(0, 1000000))
);
}
}
}
-
FeaturesDemoResource
是此示例代码中的项目名称。有关本地化系统的详细信息,请参见本地化文档。 -
第一个特征设置为
ToggleStringValueType
,第二个特征设置为FreeTextStringValueType
,它带有一个数值验证器,允许从0
到1,000,000
的值。
记住要在本地化文件中定义本地化键:
"PdfReporting": "PDF Reporting",
"MaxProductCount": "Maximum number of products"
有关本地化系统的详细信息,请参见本地化文档。
特征管理模式该应用程序启动模板随附了租户管理和特征管理模块。
每当您定义新特征时,它都会在特征管理模式下可用。要打开此模式,请导航至租户管理页面,然后为租户选择Features
操作(如果还没有租户,则创建一个新的租户):
[外链图片转存中…(img-D47Bb0uc-1599985883477)]
该动作将打开一个模式来管理所选租户的特征值:
[外链图片转存中…(img-G7IAOqIn-1599985883483)]
因此,您可以启用、禁用和设置租户的值。每当此租户的用户使用该应用程序时,将使用这些值。
请参阅下面的 特征管理 部分,以了解有关管理特征的更多信息。
子特征特征可能具有子特征。如果要创建仅在启用另一个特征后才可选择的特征,则此特征特别有用。
示例:定义子特征
using FeaturesDemo.Localization;
using Volo.Abp.Features;
using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues;
namespace FeaturesDemo
{
public class MyFeatureDefinitionProvider : FeatureDefinitionProvider
{
public override void Define(IFeatureDefinitionContext context)
{
var myGroup = context.AddGroup("MyApp");
var reportingFeature = myGroup.AddFeature(
"MyApp.Reporting",
defaultValue: "false",
displayName: LocalizableString
.Create("Reporting"),
valueType: new ToggleStringValueType()
);
reportingFeature.CreateChild(
"MyApp.PdfReporting",
defaultValue: "false",
displayName: LocalizableString
.Create("PdfReporting"),
valueType: new ToggleStringValueType()
);
reportingFeature.CreateChild(
"MyApp.ExcelReporting",
defaultValue: "false",
displayName: LocalizableString
.Create("ExcelReporting"),
valueType: new ToggleStringValueType()
);
}
}
}
上面的示例定义了具有两个子项的Reporting特征:PDF Reporting和Excel Reporting。
更改从属模块的特征定义从FeatureDefinitionProvider
派生的类(就像上面的示例一样)也可以获取现有的权限定义(由依赖模块定义)并更改其定义。
示例:操纵现有特征定义
var someGroup = context.GetGroupOrNull("SomeModule");
var feature = someGroup.Features.FirstOrDefault(f => f.Name == "SomeFeature");
if (feature != null)
{
feature.Description = ...
feature.CreateChild(...);
}
在客户端检查特征
除非您在特征定义上设置IsVisibleToClients
为false
,否则特征值也可在客户端使用。特征值从应用程序配置API公开,可以通过UI上的某些服务使用。
使用abp.features
API获取特征值。
示例:在JavaScript代码中获取特征值
var isEnabled = abp.features.values["MyApp.ExcelReporting"] === "true";
var count = abp.features.values["MyApp.MaxProductCount"];
Angular UI
请参阅Angular UI 的特征文档。
特征管理特征管理通常由管理员用户使用特征管理模式完成:
[外链图片转存中…(img-RhhSG1XH-1599985883488)]
该模式可用于相关实体,例如多租户应用程序中的租户。要打开它,请导航到“租户管理”页面(对于多租户应用程序),单击“租户”左侧的“ 操作”按钮,然后选择“特征”操作。
如果需要通过代码管理特征,请注入IFeatureManager
服务。
示例:为租户启用PDF报告
public class MyService : ITransientDependency
{
private readonly IFeatureManager _featureManager;
public MyService(IFeatureManager featureManager)
{
_featureManager = featureManager;
}
public async Task EnablePdfReporting(Guid tenantId)
{
await _featureManager.SetForTenantAsync(
tenantId,
"MyApp.PdfReporting",
true.ToString()
);
}
}
IFeatureManager
由特征管理模块定义。它预装有应用程序启动模板。有关更多信息,请参阅特征管理模块文档。
特征系统是可扩展的。任何派生自FeatureValueProvider
(或实现IFeatureValueProvider
)的类都可以对特征系统做出贡献。值提供程序负责获取给定特征的当前值。
特征值提供程序是逐一执行的。如果其中一个返回非空值,则使用此特征值,并且不执行其他提供程序。
有三个预定义的值提供程序,它们按给定的顺序执行:
-
TenantFeatureValueProvider
尝试获取是否为当前租户显式设置了特征值。 -
EditionFeatureValueProvider
尝试获取当前版本的特征值。版本ID是从当前的主体标识(ICurrentPrincipalAccessor
)和声明名称editionid
(定义为AbpClaimTypes.EditionId
的常数)获得的。租户管理模块未实现版本。您可以自己实现它,也可以考虑使用ABP 商业版 的SaaS模块。 -
DefaultValueFeatureValueProvider 获取特征的默认值。
您可以通过继承FeatureValueProvider
编写自己的提供程序。
示例:使用“SystemAdmin”作为“User_Type”声明值为用户启用所有特征
using System.Threading.Tasks;
using Volo.Abp.Features;
using Volo.Abp.Security.Claims;
using Volo.Abp.Validation.StringValues;
namespace FeaturesDemo
{
public class SystemAdminFeatureValueProvider : FeatureValueProvider
{
public override string Name => "SA";
private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
public SystemAdminFeatureValueProvider(
IFeatureStore featureStore,
ICurrentPrincipalAccessor currentPrincipalAccessor)
: base(featureStore)
{
_currentPrincipalAccessor = currentPrincipalAccessor;
}
public override Task GetOrNullAsync(FeatureDefinition feature)
{
if (feature.ValueType is ToggleStringValueType &&
_currentPrincipalAccessor.Principal?.FindFirst("User_Type")?.Value == "SystemAdmin")
{
return Task.FromResult("true");
}
return null;
}
}
}
如果提供程序返回null
,则执行下一个提供程序。
定义提供程序后,应将其添加到AbpFeatureOptions
,如下所示:
Configure(options =>
{
options.ValueProviders.Add();
});
在您的模块类的ConfigureServices
中使用此代码。
IFeatureStore
是唯一需要从持久性源(通常是数据库系统)读取特征值的接口。特征管理模块将实现它并预安装在应用程序启动模板中。有关更多信息,请参阅特征管理模块文档。