文檔目錄 本節內容: 簡介 關於 IFeatureValueStore 功能類型 Boolean 功能 Value 功能 定義功能 基本功能屬性 其它功能屬性 功能層次 檢查功能 使用RequiresFeature特性 RequiresFeature特性註意事項 使用 IFeatureChecker ...
本節內容:
大部分SaaS(多租戶)應用有不同功能的版本(包),因此你可以提供不同價格和功能選項給租戶(客戶)。
ABP提供了一個功能系統,使它易於使用,我們可以定義功能,檢查一個功能對於一個租戶是否可用,並把功能系統整合到其它ABP概念里(如授權和導航)。
功能系統使用IFeatureValueStore來獲取功能的值。儘管你可以用自己的方式實現它,但在module-zero項目里已經完全實現。如果它沒有被實現,NullFeatureValueStore用來為所有功能返回null(這種情況下使用預設功能值)。
有兩個基本的功能類型。
可以是“true”或“false”,一個這種類型的功能可以是啟用或禁用(為一個版本或一個租戶)。
可以是任意值,它保存和獲取一個字元串,數字也保存成字元串。
例如,我們的應用可能是一個任務管理應用,一個月只創建有限的幾個任務,假設我們有兩個不同的版本/包,有一個允許創建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的子功能)。
小建議:創建一個字元串常量作為一個功能名,不管在哪裡使用都可避免輸入錯誤。
雖然唯一名稱和預設值屬性是必須的,但也有一可選的屬性,提供細節控制。
- Scope:一個FeatureScopes枚舉值,它可以是Edition(如果這個功能只能設置版本級別),Tenant(如果這個功能只能設置租戶級別)或All(如果這個功能可設置版本和租戶,租戶設置會覆蓋版本設置)。預設值是All。
- DisplayName:一個本地化的字元串,為用戶顯示這個功能的名稱。
- Description:一個本地化的字元串,為客戶顯示這個功能的細節描述。
- InputType:這個功能的一個UI輸入類型,這個可被定義,當創建一個自動功能屏幕時可以使用它。
- Attribute:一個鍵值對的用戶字典,關聯這個功能。
讓我們看一下上面那個功能更多的細節定義:
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不強制但建議這麼做,應用應當小心處理它。
我們定義一個功能來檢查它的在應用里的值,從而為每個租戶允許或阻止一些應用功能。有幾種不同的檢查方式。
我們可以為一個方法或類使用RequiredFeature,如下所示:
[RequiresFeature("ExportToExcel")] public async Task<FileDto> GetReportToExcel(...) { ... }
只有當前租戶(從IAbpSession里獲取)的“ExportToExcel”功能可用時,才能執行這個方法,如果不可用,就自動拋出一個AbpAuthorizationException。
當然,RequiresFeature特性應該用在Boolean類型的功能上,否則,你會收到異常。
ABP為功能檢查使用強大的動態方法攔截,所以在方法上使用RequiresFeature特性有些限制:
- 不能用在private方法上。
- 不能用在靜態方法上。
- 不能用在無註入類的方法上(我們必須使用依賴註入)。
同時,可用於:
- 任何通過介面調用的public方法(如通過介面使用應用服務)
- 一個直接通過類引用(如Asp.Net Mvc或Web Api控制器)調用的virtual方法。
- 一個protected virtual方法。
我們可以註入IFeatureChecker,並使用它手動檢查一個功能(它被自動註入到應用服務,Mvc和Web Api控制器,並被自動使用)。
簡單的檢查一個給定的功能是否可用,如:
public async Task<FileDto> GetReportToExcel(...) { if (await FeatureChecker.IsEnabledAsync("ExportToExcel")) { throw new AbpAuthorizationException("You don't have this feature: ExportToExcel"); } ... }
IsEnabledAsync和其它方法同樣有非同步版本。
當然,IsEnabled方法應當被Boolean類型的功能使用,否則,你會得到異常。
如果你只是想檢查一個功能,並拋出異常,如上面例子所示那樣,你可以使用CheckEnabled方法。
獲取一個值類型功能的當前值,例如:
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命名空間來獲取功能的當前值。
var isEnabled = abp.features.isEnabled('SampleBooleanFeature');
var value = abp.features.getValue('SampleNumericFeature');
如果你需要用到功能的定義,你可以註入IFeatureManager並使用它。
ABP框架沒有一個內容的版本系統,因為如此一個系統需要一個資料庫(存儲版本,版本功能,租戶版本映射...),因此,版本系統在module zero里實現,你可以很容易的使用它並獲取一個版本系統,或者你自己實現。