資料校驗

小脑虎爱学习發表於2024-08-01

返回總目錄《一步一步使用ABP框架搭建正式專案系列教程》


校驗介紹

一個應用的輸入應該首先要驗證。這個輸入可以是使用者的輸入,也可以是另一個應用的輸入。在一個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。

image

image

方法執行成功,資料庫中也成功新增了資料。

現在我們不給這三個必填欄位之一賦值,修改CityController程式碼如下:

public ActionResult Create()
{
    var input = new CreateCityInput()
    {
        Name = "台州",
        //ProvinceCode = "1",
        Code = "3",
    };
    _cityAppService.CreateCity(input);
    return Content("OK");
}

image

結果報錯了,錯誤是“方法實參無效!請看驗證錯誤細節。”可見,新增資料註解的屬性因為不符合條件而產生的錯誤被成功攔截。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");
}

這裡程式碼很簡單,不用多做解釋,(有問題的話直接評論區提問),直接測試一下。

image

 

標準化

標準化就是在驗證之後,進行一些額外的操作。其實前面的程式碼已經標準化了,ABP定義了一個

具有Normalize方法的IShouldNormalize介面。如果實現了這個介面,Normalize方法就會在驗證之後呼叫。正如之前的示例程式碼:

public void Normalize()
{
    if (UpdatedTime==null)
    {
        UpdatedTime=DateTime.Now;
    }
}

這個作用就是,資料驗證之後,如果UpdatedTime屬性值為null,那麼就把當前時間給它。當然,客戶端傳過來的資料也可能給UpdatedTime賦值,這樣Normalize方法就不會執行了。

相關文章