ASP.NET Core 自訂進階驗證 IValidatableObject

在 ASP.NET Core 中我們可以在繼承 ControllerBase 的 Controller 中的參數或是傳入的物件中的成員屬性(Property)上加上 ValidationAttribute 相關的驗證屬性(Attribute),就可以自動驗證。

除了 Required, StringLength, Range 這種內建的屬性以外,還可以建立繼承 ValidationAttribute 的自訂類別製作自定義的驗證屬性。對於要重複驗證的內容來說非常的好用,不過如果同時要驗證兩個或以上的屬性(Property)就很麻煩了,這時候通常大家會轉向使用隔壁的第三方套件: FluentValidation

而 ASP.NET Core 支援的內建驗證方式除了一開始提到的 ValidationAttribute 屬性(Attribute)驗證外,還有提供專門針對單一類別自訂的 IValidatableObject 驗證,在驗證中也有辦法存取其他 Service 服務,也可以讀取資料庫內容,非常方便。

範例

建立一個 User 類別,實作 IValidatableObject 介面,多了一個 Validate 的方法,可以使用 yield return 回傳 ValidationResult ,代表驗證有問題。

下面的範例是依照 2023 年前民法上規定需要年滿 20 歲或是已結婚就是完全行為能力人,和驗證姓名不可以空白,以此示範使用 IValidatableObject 同時驗證多屬性和多驗證項目。
    
public class User : IValidatableObject
{
    public string Name { get; set; }

    /// <summary>
    /// 年紀
    /// </summary>
    public int Age { get; set; }

    /// <summary>
    /// 已結婚
    /// </summary>
    public bool IsMarried { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Age <= 20 && !IsMarried)
        {
            yield return new ValidationResult("不具有完全行為能力,無法買房", new[] { nameof(Age), nameof(IsMarried) });
        }

        if (string.IsNullOrWhiteSpace(Name))
        {
            yield return new ValidationResult("名字不可為空白", new[] { nameof(Name) });
        }
    }
}
    

Controller
    
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase
{
    [HttpPost]
    public ActionResult Get([FromBody] User dto)
    {
        return Ok(dto);
    }
}
    

範例呼叫:
    
{
  "name": "",
  "age": 0,
  "isMarried": false
}
    

範例回應內容:
    
{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-916d57cbeff7215f7edb2ea406c6c4bd-c35c1deac6c6901d-00",
  "errors": {
    "Age": [
      "不具有完全行為能力,無法買房"
    ],
    "Name": [
      "名字不可為空白"
    ],
    "IsMarried": [
      "不具有完全行為能力,無法買房"
    ]
  }
}
    

存取服務和資料庫

要存取服務非常簡單,可以使用 validationContext.GetService 取得服務,這裡使用 Entity Framework Core 7 的 ApplicationDbContext 做示範,假設有 Id 屬性和 Users 資料表,就可以這樣驗證:
    
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    var applicationDbContext = validationContext.GetService<ApplicationDbContext>();
    var entity = applicationDbContext.Users
        .FirstOrDefault(x => x.Id == Id);

    if (entity == null)
    {
        yield return new ValidationResult("查無此使用者", new[] { nameof(Id) });
    }
}
    



參考資料:
Microsoft.Learn - Model validation in ASP.NET Core MVC and Razor Pages

留言