校驗介紹
一個應用的輸入應該首先要驗證。這個輸入可以是用戶的輸入,也可以是另一個應用的輸入。在一個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方法就不會執行了。