參數有效性驗證
應用程式的輸入資料首先應該被檢驗是否有效。輸入的資料能被使用者或其他應用程式提交。在Web應用中,通常進行2次資料有效性檢驗:包括用戶端檢驗和服務端檢驗。用戶端的檢驗主要是使使用者有一個好的使用者體驗。 首先最好是在用戶端檢驗其表單輸入的有效性並且展示給用戶端的那些欄位輸入是無效的。但是,伺服器端的校正是更關鍵和不可缺失的(不要只做用戶端檢驗而不做伺服器端檢驗)。
伺服器端的檢驗通常是被應用服務(層)執行,應用服務(層)中的方法首先檢驗資料的有效性,然後才使用這些通過驗證的資料。ABP的基礎設施提供了自動檢驗輸入資料有效性的方法。
應用服務(層)方法得到一個資料轉送對象(DTO)作為輸入。ABP有一個IValidate的介面,DTO通過實現這個介面能夠檢驗資料的有效性。由於IInputDto擴充自IValidate,所以你可以直接實現IInputDto 介面來對資料轉送對象(DTO)檢驗其有效性。
使用資料註解
ABP提供資料註解的特性。假設我們正在開發一個建立任務的應用服務並且得到了一個輸入,請看下面樣本:
public class CreateTaskInput : IInputDto{ public int? AssignedPersonId { get; set; } [Required] public string Description { get; set; }}
在這裡,Description 屬性被標記為 Required。AssignedPersonId 是可選的。在 System.ComponentModel.DataAnnotations 命名空間中,還有很多這樣的特性 ( 例如: MaxLength, MinLength, RegularExpression 等等 )。
在System.ComponentModel.DataAnnotations 命名空間中,請看Task application service 的實現
public class TaskAppService : ITaskAppService{ private readonly ITaskRepository _taskRepository; private readonly IPersonRepository _personRepository; public TaskAppService(ITaskRepository taskRepository, IPersonRepository personRepository) { _taskRepository = taskRepository; _personRepository = personRepository; } public void CreateTask(CreateTaskInput input) { var task = new Task { Description = input.Description }; if (input.AssignedPersonId.HasValue) { task.AssignedPerson = _personRepository.Load(input.AssignedPersonId.Value); } _taskRepository.Insert(task); }}
正如你所看到的,這裡沒有寫任何的資料驗證性代碼(指對Description屬性的驗證)因為ABP會自動去檢驗資料的有效性。ABP也會檢驗輸入資料是否為null。如果為空白則會拋出AbpValidationException 異常。所以你不需要寫檢驗資料是否為null值的代碼。如果有任何屬性的輸入資料是無效的它也會拋出相同的異常。
這個機制近似於 ASP.NET MVC 的驗證功能,注意:這裡的應用服務類不是繼承自Controller,它是用在Web應用的一個普通類。
自訂檢驗
如果資料註解的方式不能滿足你的需求,你可以實現ICustomValidate介面,請看下面樣本:
public class CreateTaskInput : IInputDto, ICustomValidate{ public int? AssignedPersonId { get; set; } public bool SendEmailToAssignedPerson { get; set; } [Required] public string Description { get; set; } public void AddValidationErrors(List<ValidationResult> results) { if (SendEmailToAssignedPerson && (!AssignedPersonId.HasValue || AssignedPersonId.Value <= 0)) { results.Add(new ValidationResult("AssignedPersonId must be set if SendEmailToAssignedPerson is true!")); } }}
ICustomValidate 介面聲明了一個可被實現的AddValidationErrors方法。這裡我們有一個叫做 SendEmailToAssignedPerson 的屬性。如果該屬性是真,AssignedPersonId 屬性會被檢驗是否有效,否則該屬性可以為空白。如果有驗證錯誤,我們必須添加把這些驗證結果添加到結果集合中。(就是將ValidationResult 添加到results)
設定預設值
在檢驗資料有效性後,我們需要執行一個額外的操作來整理DTO參數。ABP定義了一個IShouldNormalize介面,這個介面聲明了一個 Normalize的方法。如果你實現了這個介面,在檢驗資料有效性後,Normalize方法會被調用。假設我們的DTO需要一個排序方向的資料。如果這個Sorting屬性沒有被提供資料,那麼在Normalize我們可以給Sorting設定一個預設值。
public class GetTasksInput : IInputDto, IShouldNormalize{ public string Sorting { get; set; } public void Normalize() { if (string.IsNullOrWhiteSpace(Sorting)) { Sorting = "Name ASC"; } }}
許可權驗證
幾乎所有的企業級應用程式都會有不同層級的許可權驗證。許可權驗證是用於檢查使用者是否允許某些指定操作。Abp有基礎設施讓你來實現許可權驗證。
注意:關於IPermissionChecker介面
Abp許可權系統使用IPermissionChecker去檢查授權。同時你可以根據需要實現你自己的方式,在module-zero項目中已經完整實現了。如果IPermissionChecker沒有被實現,NullPermissionChecker會被使用於授權所有許可權給每個人。
定義許可權
在使用驗證許可權前,我們需要為每一個操作定義唯一的許可權。Abp的設計是基於模組化,所以不同的模組可以有不同的許可權。為了定義許可權,一個模組應該建立AuthorizationProvider的衍生類別。MyAuthorizationProvider繼承自AuthorizationProvider,換句話說就是AuthorizationProvider派生出MyAuthorizationProvider。例子如下:
public class MyAuthorizationProvider : AuthorizationProvider{ public override void SetPermissions(IPermissionDefinitionContext context) { var administration = context.CreatePermission("Administration"); var userManagement = administration.CreateChildPermission("Administration.UserManagement"); userManagement.CreateChildPermission("Administration.UserManagement.CreateUser"); var roleManagement = administration.CreateChildPermission("Administration.RoleManagement"); }}
IPermissionDefinitionContext 有方法去擷取和建立許可權。
一個許可權有以下屬性:
- Name:系統範圍內的唯一名字。把它定義為一個字串常量是個不錯的注意。我們傾向於將“.”分割不同的層級,但並不要求這麼做。你可以設定你任何喜歡的名字。唯一的規則就是這個名字必須是唯一的。
- Display Name:使用一個本地化的字串去顯示許可權到UI。
- Description:和Display Name類似。
- IsGrantedByDefault:此許可權是否授權給(已登陸)所有使用者,除非顯示指定。通常設定為False(預設值)。
- MultiTenancySides:對租戶應用程式,一個許可權可以基於租戶或者主機(原文:host)。這是個枚舉標識,因此許可權可以應用於不同方面(原文:Both Sides)。
一個許可權可以有父許可權和子許可權。當然,這不會影響許可權檢查,它只是在UI層對許可權歸類有好處。建立authorizationprovider之後,我們應該在模組的PreIntialize方法對它進行註冊。如下:
Configuration.Authorization.Providers.Add<MyAuthorizationProvider>()
authorizationprovider會自動註冊到依賴注入系統中。因此,authorization provider可以注入任何依賴(像是Repository)從而使用其他資源去建立許可權定義。
檢查許可權
1.使用AbpAuthorize特性(Using AbpAuthorize attribute)
AbpAuthorize(AbpMvcAuthorize 對應 MVC Controllers and AbpApiAuthorize 對應 Web API Controllers)特性是最簡單和常用的方法去檢查許可權。請考慮如下application service方法:
[AbpAuthorize("Administration.UserManagement.CreateUser")]public void CreateUser(CreateUserInput input){ //A user can not execute this method if he is not granted for "Administration.UserManagement.CreateUser" permission.}
沒有獲得“Administration.UserManagement.CreateUser”許可權的使用者不能夠調用CreateUser。
AbpAuthorize 特性也檢查目前使用者是否登入 (使用 IAbpSession.UserId)。因此,如果我們將某個方法聲明為AbpAuthorize 特性,它至少會檢查使用者是否登入。代碼如下: [AbpAuthorize]
public void SomeMethod(SomeMethodInput input){ //A user can not execute this method if he did not login.}
2.AbpAuthorize屬性說明(AbpAuthorize attribute notes)
Abp使用動態方法攔截進行許可權驗證。因此,使用AbpAuthorize特性的方法會有些限制。如下:
不能應用於私人(private)方法
不能應用於靜態(static)方法
不能應用於非注入(non-injected)類(我們必須用依賴注入)。
此外,
AbpAuthorize特性可以應用於任何的Public方法,如果此方法被介面調用(比如在Application Services中通過介面調用)
方法是虛(virtual)方法,如果此方法直接被類引用進行調用(像是ASP.NET MVC 或 Web API 的控制器)。
方式是虛(virtual)方法,如果此方法是protected。
注意:有三種AbpAuthorize 特性:
(1)在應用程式服務中(application layer),我們使用Abp.Authorization.AbpAuthorize;
(2)在MVC控制器(web layer)中,我們使用Abp.Web.Mvc.Authorization.AbpMvcAuthorize;
(3)在ASP.NET Web API,我們使用 Abp.WebApi.Authorization.AbpApiAuthorize。
這三個類繼承自不同的地方。
在MVC中,它繼承自MVC自己的Authorize類。
在Web API,它繼承自Web API 的Authorize類。因此,它最好是繼承到MVC和Web API中。
但是,在Application 層,它完全是由Abp自己實現沒有擴充子任何類。
3.使用IPermissionChecker
AbpAuthorize 適用於大部分的情況,但是某些情況下,我們還是需要自己在方法體裡進行許可權驗證。我們可以注入和使用IPermissionChecker對象。如下邊的代碼所示:
public void CreateUser(CreateOrUpdateUserInput input){ if (!PermissionChecker.IsGranted("Administration.UserManagement.CreateUser")) { throw new AbpAuthorizationException("You are not authorized to create user!"); } //A user can not reach this point if he is not granted for "Administration.UserManagement.CreateUser" permission.}
當然,你可以寫入任何邏輯,由於IsGranted方法只是簡單返回true或false(它還有非同步版本哦)。如你簡單的檢查一個許可權並拋出一個異常如上邊代碼那樣,你可以用Authorize方法:
public void CreateUser(CreateOrUpdateUserInput input){ PermissionChecker.Authorize("Administration.UserManagement.CreateUser"); //A user can not reach this point if he is not granted for "Administration.UserManagement.CreateUser" permission.}
由於許可權驗證通常實現與Application層,ApplicationService基礎類注入和定義了PermissionChecker屬性。因此,許可權檢查器允許你在Application Service類使用,而不需要顯示注入。