文檔目錄
本節內容:
簡介
大部分SaaS(多租戶)應用有不同功能的版本(包),因此你可以提供不同價格和功能選項給租戶(客戶)。
ABP提供了一個功能系統,使它易於使用,我們可以定義功能,檢查一個功能對於一個租戶是否可用,並把功能系統整合到其它ABP概念裡(如授權和導航)。
關於 IFeatureValueStore
功能系統使用IFeatureValueStore來獲取功能的值。盡管你可以用自己的方式實現它,但在module-zero項目裡已經完全實現。如果它沒有被實現,NullFeatureValueStore用來為所有功能返回null(這種情況下使用默認功能值)。
功能類型
有兩個基本的功能類型。
Boolean 功能
可以是“true”或“false”,一個這種類型的功能可以是啟用或禁用(為一個版本或一個租戶)。
Value 功能
可以是任意值,它保存和獲取一個字符串,數字也保存成字符串。
例如,我們的應用可能是一個任務管理應用,一個月只創建有限的幾個任務,假設我們有兩個不同的版本/包,有一個允許創建1000個任務每個月,但另一個允許我們創建5000個任務每個月,所以這個功能應當存成值類型,不是簡單的true/false。
定義功能
在檢查功能前,先要定義它,一個模塊可通過繼承FeatureProvider類來定義自己的功能,此處,有一個非常簡單的功能供應器定義了3個功能:
public class AppFeatureProvider : FeatureProvider { public override void SetFeatures(IFeatureDefinitionContext context) { var sampleBooleanFeature = context.Create("SampleBooleanFeature", defaultValue: "false"); sampleBooleanFeature.CreateChildFeature("SampleNumericFeature", defaultValue: "10"); context.Create("SampleSelectionFeature", defaultValue: "B"); } }
在創建一個功能供應器之後,我們應當在我們模塊的PreInitialize方法裡注冊它,如下所示:
Configuration.Features.Providers.Add<AppFeatureProvider>();
基本功能屬性
一個功能定義要求至少兩個屬性:
Name:一個唯一名稱(字符串),這個功能的標志。
DefaultValue:一個默認值,當我們需要這個功能的值而又不能從當前租戶取得時,我們需要一個默認值。
此處,我們定義了一個Boolean功能,名為“SampleBooleanFeature”,默認值為“false”(不可用),同時我們定義了兩個值功能(SampleNumericFeature被定義成SampleBooleanFeature的子功能)。
小建議:創建一個字符串常量作為一個功能名,不管在哪裡使用都可避免輸入錯誤。
其它功能屬性
雖然唯一名稱和默認值屬性是必須的,但也有一可選的屬性,提供細節控制。
讓我們看一下上面那個功能更多的細節定義:
public class AppFeatureProvider : FeatureProvider { public override void SetFeatures(IFeatureDefinitionContext context) { var sampleBooleanFeature = context.Create( AppFeatures.SampleBooleanFeature, defaultValue: "false", displayName: L("Sample boolean feature"), inputType: new CheckboxInputType() ); sampleBooleanFeature.CreateChildFeature( AppFeatures.SampleNumericFeature, defaultValue: "10", displayName: L("Sample numeric feature"), inputType: new SingleLineStringInputType(new NumericValueValidator(1, 1000000)) ); context.Create( AppFeatures.SampleSelectionFeature, defaultValue: "B", displayName: L("Sample selection feature"), inputType: new ComboboxInputType( new StaticLocalizableComboboxItemSource( new LocalizableComboboxItem("A", L("Selection A")), new LocalizableComboboxItem("B", L("Selection B")), new LocalizableComboboxItem("C", L("Selection C")) ) ) ); } private static ILocalizableString L(string name) { return new LocalizableString(name, AbpZeroTemplateConsts.LocalizationSourceName); } }
注意:輸入類型定義不被ABP所用,當創建功能的輸入時,它可被應用使用,ABP只是提供基礎框架,使它更易於使用。
功能層次
如上面所示的示例功能供應器,一個功能可以有子功能。一個父功能通常定義為Boolean功能,只有在父功能可用時,才能獲取子功能。ABP不強制但建議這麼做,應用應當小心處理它。
檢查功能
我們定義一個功能來檢查它的在應用裡的值,從而為每個租戶允許或阻止一些應用功能。有幾種不同的檢查方式。
使用RequiresFeature特性
我們可以為一個方法或類使用RequiredFeature,如下所示:
[RequiresFeature("ExportToExcel")] public async Task<FileDto> GetReportToExcel(...) { ... }
只有當前租戶(從IAbpSession裡獲取)的“ExportToExcel”功能可用時,才能執行這個方法,如果不可用,就自動拋出一個AbpAuthorizationException。
當然,RequiresFeature特性應該用在Boolean類型的功能上,否則,你會收到異常。
RequiresFeature特性注意事項
ABP為功能檢查使用強大的動態方法攔截,所以在方法上使用RequiresFeature特性有些限制:
同時,可用於:
使用 IFeatureChecker
我們可以注入IFeatureChecker,並使用它手動檢查一個功能(它被自動注入到應用服務,Mvc和Web Api控制器,並被自動使用)。
IsEnabled
簡單的檢查一個給定的功能是否可用,如:
public async Task<FileDto> GetReportToExcel(...) { if (await FeatureChecker.IsEnabledAsync("ExportToExcel")) { throw new AbpAuthorizationException("You don't have this feature: ExportToExcel"); } ... }
IsEnabledAsync和其它方法同樣有異步版本。
當然,IsEnabled方法應當被Boolean類型的功能使用,否則,你會得到異常。
如果你只是想檢查一個功能,並拋出異常,如上面例子所示那樣,你可以使用CheckEnabled方法。
GetValue
獲取一個值類型功能的當前值,例如:
var createdTaskCountInThisMonth = GetCreatedTaskCountInThisMonth(); if (createdTaskCountInThisMonth >= FeatureChecker.GetValue("MaxTaskCreationLimitPerMonth").To<int>()) { throw new AbpAuthorizationException("You exceed task creation limit for this month, sorry :("); }
FeatureChecker方法也提供了為一個指定tenantId工作的功能,不只是為當前tenantId。
客戶端
在客戶端(Javascript),我們可以使用abp.features命名空間來獲取功能的當前值。
isEnabled
var isEnabled = abp.features.isEnabled('SampleBooleanFeature');
getValue
var value = abp.features.getValue('SampleNumericFeature');
功能管理器
如果你需要用到功能的定義,你可以注入IFeatureManager並使用它。
對版本的一個提示
ABP框架沒有一個內容的版本系統,因為如此一個系統需要一個數據庫(存儲版本,版本功能,租戶版本映射...),因此,版本系統在module zero裡實現,你可以很容易的使用它並獲取一個版本系統,或者你自己實現。