Abp vnext 特征(Features)

简介

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<PdfReportResultDto> GetPdfReportAsync()
    {
        //TODO...
    }
}
  • RequiresFeature(...)只需获取特征名称即可检查它是否已启用。如果未启用,则将抛出授权异常,并将适当的响应返回给客户端。

  • [RequiresFeature]可以用于方法。当您将它用于一个类时,该类的所有方法都需要给定的特征。

  • RequiresFeature可能会获得多个特征名称,例如[RequiresFeature("Feature1", "Feature2")]。在这种情况下,ABP检查是否启用了任何特征。使用RequiresAll选项,例如[RequiresFeature("Feature1", "Feature2", RequiresAll = true)]强制检查要启用的所有特征。

  • 方法或类支持多次使用[RequiresFeature]属性。在这种情况下,ABP check会检查所有它们。

特征名称可以是任意字符串。对于某个特征,它应该是唯一的。

关于拦截

ABP框架使用拦截系统使[RequiresFeature]属性正常工作。因此,它可以与从依赖注入注入的任何类(应用程序服务,控制器…)一起使用。

但是,为了使它起作用,应该遵循一些规则

  • 如果您不是通过接口(如IMyService注入服务,则该服务的方法必须为virtual。否则,动态代理/拦截系统将无法工作。

  • 仅拦截async方法(返回一个TaskTask<T>的方法)。

控制器和razor页面方法有一个例外。它们不需要上述规则,因为在这种情况下,ABP Framework使用动作/页面过滤器来实现特征检查。

IFeatureChecker服务

IFeatureChecker允许检查您的应用程序代码中的特征。

IsEnabledAsync

如果启用给定特征则返回true。因此,您可以有条件地执行业务流程。

示例:检查是否启用了“PDF报告”特征

public class ReportingAppService : ApplicationService, IReportingAppService
{
    private readonly IFeatureChecker _featureChecker;

    public ReportingAppService(IFeatureChecker featureChecker)
    {
        _featureChecker = featureChecker;
    }

    public async Task<PdfReportResultDto> GetPdfReportAsync()
    {
        if (await _featureChecker.IsEnabledAsync("MyApp.PdfReporting"))
        {
            //TODO...
        }
        else
        {
            //TODO...   
        }
    }
}

IsEnabledAsync 通过重载在一个方法调用中检查多个特征。

GetOrNullAsync

获取特征的当前值。此方法返回string,因此你可以通过转换string来存储任何类型的值。

示例:检查允许的最大产品数量

public class ProductController : AbpController
{
    private readonly IFeatureChecker _featureChecker;

    public ProductController(IFeatureChecker featureChecker)
    {
        _featureChecker = featureChecker;
    }

    public async Task<IActionResult> 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<int> GetCurrentProductCountFromDatabase()
    {
        throw new System.NotImplementedException();
    }
}

本示例使用数字值作为SaaS应用程序中用户/租户的特征限制产品计数。

您可以使用GetAsync方法的一般重载,而不是将值手动转换为int

var maxProductCountLimit = await _featureChecker.GetAsync<int>("MyApp.MaxProductCount");

扩展方法

IFeatureChecker接口有一些有用的扩展方法。

  • Task<T> GetAsync<T>(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/falseon/offenabled/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<FeaturesDemoResource>("PdfReporting"),
                valueType: new ToggleStringValueType()
            );

            myGroup.AddFeature(
                "MyApp.MaxProductCount",
                defaultValue: "10",
                displayName: LocalizableString
                                 .Create<FeaturesDemoResource>("MaxProductCount"),
                valueType: new FreeTextStringValueType(
                               new NumericValueValidator(0, 1000000))
            );
        }
    }
}
  • FeaturesDemoResource是此示例代码中的项目名称。有关本地化系统的详细信息,请参见本地化文档

  • 第一个特征设置为ToggleStringValueType,第二个特征设置为FreeTextStringValueType,它带有一个数值验证器,允许从01,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<FeaturesDemoResource>("Reporting"),
                valueType: new ToggleStringValueType()
            );

            reportingFeature.CreateChild(
                "MyApp.PdfReporting",
                defaultValue: "false",
                displayName: LocalizableString
                                 .Create<FeaturesDemoResource>("PdfReporting"),
                valueType: new ToggleStringValueType()
            );

            reportingFeature.CreateChild(
                "MyApp.ExcelReporting",
                defaultValue: "false",
                displayName: LocalizableString
                                 .Create<FeaturesDemoResource>("ExcelReporting"),
                valueType: new ToggleStringValueType()
            );
        }
    }
}

上面的示例定义了具有两个子项的Reporting特征PDF ReportingExcel Reporting

更改从属模块的特征定义

FeatureDefinitionProvider派生的类(就像上面的示例一样)也可以获取现有的权限定义(由依赖模块定义)并更改其定义。

示例:操纵现有特征定义

var someGroup = context.GetGroupOrNull("SomeModule");
var feature = someGroup.Features.FirstOrDefault(f => f.Name == "SomeFeature");
if (feature != null)
{
    feature.Description = ...
    feature.CreateChild(...);
}

在客户端检查特征

除非您在特征定义上设置IsVisibleToClientsfalse,否则特征值也可在客户端使用。特征值从应用程序配置API公开,可以通过UI上的某些服务使用。

ASP.NET Core MVC / Razor页面用户界面

使用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<string> GetOrNullAsync(FeatureDefinition feature)
        {
            if (feature.ValueType is ToggleStringValueType &&
                _currentPrincipalAccessor.Principal?.FindFirst("User_Type")?.Value == "SystemAdmin")
            {
                return Task.FromResult("true");
            }

            return null;
        }
    }
}

如果提供程序返回null,则执行下一个提供程序。

定义提供程序后,应将其添加到AbpFeatureOptions,如下所示:

Configure<AbpFeatureOptions>(options =>
{
    options.ValueProviders.Add<SystemAdminFeatureValueProvider>();
});

在您的模块类的ConfigureServices中使用此代码。

特征仓库

IFeatureStore是唯一需要从持久性源(通常是数据库系统)读取特征值的接口。特征管理模块将实现它并预安装在应用程序启动模板中。有关更多信息,请参阅特征管理模块文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值