校驗介紹
一個應用的輸入應該首先要驗證。這個輸入可以是使用者的輸入,也可以是另一個應用的輸入。在一個Web應用中,驗證通常要實現2次:第一次是客戶端驗證,第二次是服務端驗證。客戶端的驗證是為了更好的使用者體驗,通過檢測表單的欄位來提醒使用者必須的欄位;服務端的驗證是更嚴格且無法避免的。
服務端的驗證是在應用服務層實現的。應用服務方法應該首先檢查(驗證)輸入然後在使用。ABP提供了一個不錯的基礎設施來驗證應用服務方法的輸入。
輸入服務方法以一個DTO物件作為輸入引數。ABP提供了一個DTO可以實現的IValidate介面來自動驗證它們。因為IInputDto擴充套件了IValidate,因此輸入DTOs可以只實現IInput來確保驗證。
使用資料註解
ABP支援資料註解,ABP通過MethodInvocationValidator對服務層方法引數攔截,需要實現驗證的方法,使用ValidationInterceptor進行攔截。對應的原始碼在 Abp.Runtime.Validation.Interception名稱空間。
現在我在CityInput檔案中新增一個類CreateCityInput,程式碼如下:
public class CreateCityInput : IInputDto, IShouldNormalize { [Required] public string Name { get; set; } [Required] public string Code { get; set; } [Required] public string ProvinceCode { get; set; } public DateTime UpdatedTime { get; set; } public string UpdatedBy { get; set; } public void Normalize() { if (UpdatedTime==null) { UpdatedTime=DateTime.Now; } } }
CreateCityInput類實現了IInput和IShouldNormalize介面,並且在Name,Code,ProvinceCode是必填欄位,最後實現了IShouldNormalize介面中的Normalize方法,判斷了UpdatedTime是否為null,如果是null,就賦值為當前的時間。
在ICityAppService服務介面中新增方法:
void CreateCity(CreateCityInput input);
在CityAppService中實現該介面的此方法:
public void CreateCity(CreateCityInput input) { var city = _cityRepository.FirstOrDefault(c => c.Name == input.Name); if (city != null) { throw new UserFriendlyException("該城市資料已經存在!"); } city = new Cities() { Code = input.Code, Name = input.Name, ProvinceCode = input.ProvinceCode }; _cityRepository.Insert(city); }
在CityController中新增Create方法:
public ActionResult Create() { var input = new CreateCityInput() { Name = "溫州", ProvinceCode = "1", Code = "3", }; _cityAppService.CreateCity(input); return Content("OK"); }
這裡,我們建立了一個CreateCityInput物件,並給三個必填欄位賦值,然後呼叫服務介面的方法,如果服務方法執行成功,就向頁面返回OK。
方法執行成功,資料庫中也成功新增了資料。
現在我們不給這三個必填欄位之一賦值,修改CityController程式碼如下:
public ActionResult Create() { var input = new CreateCityInput() { Name = "台州", //ProvinceCode = "1", Code = "3", }; _cityAppService.CreateCity(input); return Content("OK"); }
結果報錯了,錯誤是“方法實參無效!請看驗證錯誤細節。”可見,新增資料註解的屬性因為不符合條件而產生的錯誤被成功攔截。ABP也會檢測輸入是否為null,如果為null,就丟擲AbpValidationException異常。因此,不必寫檢測null的程式碼。如果輸入的屬性之一是無效的,也會丟擲相同的異常。
這種機制和ASP.NET MVC的驗證機制很相似,但是注意應用服務類不是派生自Controller類的,它是一個普通的類並且可以獨立於web工作。
自定義驗證
如果資料註解還不能滿足你的需求的話,你也可以實現ICustomValidate介面:
public class CreateCityInput : IInputDto, IShouldNormalize,ICustomValidate { [Required] public string Name { get; set; } [Required] public string Code { get; set; } [Required] public string ProvinceCode { get; set; } public DateTime UpdatedTime { get; set; } public string UpdatedBy { get; set; } public void Normalize() { if (UpdatedTime==null) { UpdatedTime=DateTime.Now; } } public void AddValidationErrors(List<ValidationResult> results) { if (ProvinceCode.Length>5) { results.Add(new ValidationResult("省份編碼長度不能超過5個字元!"));
throw new Exception("省份編碼長度不能超過5個字元!");
}
}
}
public ActionResult Create() { var input = new CreateCityInput() { Name = "衢州", ProvinceCode = "123456", Code = "4", }; _cityAppService.CreateCity(input); return Content("OK"); }
這裡程式碼很簡單,不用多做解釋,(有問題的話直接評論區提問),直接測試一下。
標準化
標準化就是在驗證之後,進行一些額外的操作。其實前面的程式碼已經標準化了,ABP定義了一個
具有Normalize方法的IShouldNormalize介面。如果實現了這個介面,Normalize方法就會在驗證之後呼叫。正如之前的示例程式碼:
public void Normalize() { if (UpdatedTime==null) { UpdatedTime=DateTime.Now; } }
這個作用就是,資料驗證之後,如果UpdatedTime屬性值為null,那麼就把當前時間給它。當然,客戶端傳過來的資料也可能給UpdatedTime賦值,這樣Normalize方法就不會執行了。