參數(shù)有效性驗證
應(yīng)用程序的輸入數(shù)據(jù)首先應(yīng)該被檢驗是否有效。輸入的數(shù)據(jù)能被用戶或其他應(yīng)用程序提交。在Web應(yīng)用中,通常進行2次數(shù)據(jù)有效性檢驗:包括客戶端檢驗和服務(wù)端檢驗??蛻舳说臋z驗主要是使用戶有一個好的用戶體驗。 首先最好是在客戶端檢驗其表單輸入的有效性并且展示給客戶端的那些字段輸入是無效的。但是,服務(wù)器端的校驗是更關(guān)鍵和不可缺失的(不要只做客戶端檢驗而不做服務(wù)器端檢驗)。
服務(wù)器端的檢驗通常是被應(yīng)用服務(wù)(層)執(zhí)行,應(yīng)用服務(wù)(層)中的方法首先檢驗數(shù)據(jù)的有效性,然后才使用這些通過驗證的數(shù)據(jù)。ABP的基礎(chǔ)設(shè)施提供了自動檢驗輸入數(shù)據(jù)有效性的方法。
應(yīng)用服務(wù)(層)方法得到一個數(shù)據(jù)傳輸對象(DTO)作為輸入。ABP有一個IValidate的接口,DTO通過實現(xiàn)這個接口能夠檢驗數(shù)據(jù)的有效性。由于IInputDto擴展自IValidate,所以你可以直接實現(xiàn)IInputDto 接口來對數(shù)據(jù)傳輸對象(DTO)檢驗其有效性。
使用數(shù)據(jù)注解
ABP提供數(shù)據(jù)注解的特性。假設(shè)我們正在開發(fā)一個創(chuàng)建任務(wù)的應(yīng)用服務(wù)并且得到了一個輸入,請看下面示例:
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 的實現(xiàn)
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);
}
}
正如你所看到的,這里沒有寫任何的數(shù)據(jù)驗證性代碼(指對Description屬性的驗證)因為ABP會自動去檢驗數(shù)據(jù)的有效性。ABP也會檢驗輸入數(shù)據(jù)是否為null。如果為空則會拋出AbpValidationException 異常。所以你不需要寫檢驗數(shù)據(jù)是否為null值的代碼。如果有任何屬性的輸入數(shù)據(jù)是無效的它也會拋出相同的異常。
這個機制近似于 ASP.NET MVC 的驗證功能,注意:這里的應(yīng)用服務(wù)類不是繼承自Controller,它是用在Web應(yīng)用的一個普通類。
自定義檢驗
如果數(shù)據(jù)注解的方式不能滿足你的需求,你可以實現(xiàn)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 接口聲明了一個可被實現(xiàn)的AddValidationErrors方法。這里我們有一個叫做 SendEmailToAssignedPerson 的屬性。如果該屬性是真,AssignedPersonId 屬性會被檢驗是否有效,否則該屬性可以為空。如果有驗證錯誤,我們必須添加把這些驗證結(jié)果添加到結(jié)果集合中。(就是將ValidationResult 添加到results)
設(shè)置缺省值
在檢驗數(shù)據(jù)有效性后,我們需要執(zhí)行一個額外的操作來整理DTO參數(shù)。ABP定義了一個IShouldNormalize接口,這個接口聲明了一個 Normalize的方法。如果你實現(xiàn)了這個接口,在檢驗數(shù)據(jù)有效性后,Normalize方法會被調(diào)用。假設(shè)我們的DTO需要一個排序方向的數(shù)據(jù)。如果這個Sorting屬性沒有被提供數(shù)據(jù),那么在Normalize我們可以給Sorting設(shè)置一個缺省值。
public class GetTasksInput : IInputDto, IShouldNormalize
{
public string Sorting { get; set; }
public void Normalize()
{
if (string.IsNullOrWhiteSpace(Sorting))
{
Sorting = "Name ASC";
}
}
}
權(quán)限驗證
幾乎所有的企業(yè)級應(yīng)用程序都會有不同級別的權(quán)限驗證。權(quán)限驗證是用于檢查用戶是否允許某些指定操作。Abp有基礎(chǔ)設(shè)施讓你來實現(xiàn)權(quán)限驗證。
注意:關(guān)于IPermissionChecker接口
Abp權(quán)限系統(tǒng)使用IPermissionChecker去檢查授權(quán)。同時你可以根據(jù)需要實現(xiàn)你自己的方式,在module-zero項目中已經(jīng)完整實現(xiàn)了。如果IPermissionChecker沒有被實現(xiàn),NullPermissionChecker會被使用于授權(quán)所有權(quán)限給每個人。
定義權(quán)限
在使用驗證權(quán)限前,我們需要為每一個操作定義唯一的權(quán)限。Abp的設(shè)計是基于模塊化,所以不同的模塊可以有不同的權(quán)限。為了定義權(quán)限,一個模塊應(yīng)該創(chuàng)建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 有方法去獲取和創(chuàng)建權(quán)限。
一個權(quán)限有以下屬性:
1.Name:系統(tǒng)范圍內(nèi)的唯一名字。把它定義為一個字符串常量是個不錯的注意。我們傾向于將“.”分割不同的層級,但并不要求這么做。你可以設(shè)置你任何喜歡的名字。唯一的規(guī)則就是這個名字必須是唯一的。
2.Display Name:使用一個本地化的字符串去顯示權(quán)限到UI。
3.Description:和Display Name類似。
4.IsGrantedByDefault:此權(quán)限是否授權(quán)給(已登陸)所有用戶,除非顯示指定。通常設(shè)置為False(默認值)。
5.MultiTenancySides:對租戶應(yīng)用程序,一個權(quán)限可以基于租戶或者主機(原文:host)。這是個枚舉標識,因此權(quán)限可以應(yīng)用于不同方面(原文:Both Sides)。
一個權(quán)限可以有父權(quán)限和子權(quán)限。當然,這不會影響權(quán)限檢查,它只是在UI層對權(quán)限歸類有好處。創(chuàng)建authorizationprovider之后,我們應(yīng)該在模塊的PreIntialize方法對它進行注冊。如下:
Configuration.Authorization.Providers.Add<MyAuthorizationProvider>()
authorizationprovider會自動注冊到依賴注入系統(tǒng)中。因此,authorization provider可以注入任何依賴(像是Repository)從而使用其他資源去創(chuàng)建權(quán)限定義。
檢查權(quán)限
1.使用AbpAuthorize特性(Using AbpAuthorize attribute)
AbpAuthorize(AbpMvcAuthorize 對應(yīng) MVC Controllers and AbpApiAuthorize 對應(yīng) Web API Controllers)特性是最簡單和常用的方法去檢查權(quán)限。請考慮如下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”權(quán)限的用戶不能夠調(diào)用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使用動態(tài)方法攔截進行權(quán)限驗證。因此,使用AbpAuthorize特性的方法會有些限制。如下:
不能應(yīng)用于私有(private)方法
不能應(yīng)用于靜態(tài)(static)方法
不能應(yīng)用于非注入(non-injected)類(我們必須用依賴注入)。
此外,
AbpAuthorize特性可以應(yīng)用于任何的Public方法,如果此方法被接口調(diào)用(比如在Application Services中通過接口調(diào)用)
方法是虛(virtual)方法,如果此方法直接被類引用進行調(diào)用(像是ASP.NET MVC 或 Web API 的控制器)。
方式是虛(virtual)方法,如果此方法是protected。
注意:有三種AbpAuthorize 特性:
(1)在應(yīng)用程序服務(wù)中(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自己實現(xiàn)沒有擴展子任何類。
3.使用IPermissionChecker
AbpAuthorize 適用于大部分的情況,但是某些情況下,我們還是需要自己在方法體里進行權(quán)限驗證。我們可以注入和使用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(它還有異步版本哦)。如你簡單的檢查一個權(quán)限并拋出一個異常如上邊代碼那樣,你可以用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.
}
由于權(quán)限驗證通常實現(xiàn)與Application層,ApplicationService基礎(chǔ)類注入和定義了PermissionChecker屬性。因此,權(quán)限檢查器允許你在Application Service類使用,而不需要顯示注入。