.Net Core 使用 FluentValidation

樹杈發表於2020-11-12

FluentValidation 支援與 ASP.NET Core 2.1 或3.1整合(建議使用3.1)。啟用後,MVC將使用 FluentValidation 來驗證由模型繫結基礎結構傳遞到控制器操作中的物件。

要啟用MVC整合,您需要 FluentValidation.AspNetCore 通過安裝適當的NuGet軟體包來新增對程式集的引用。

安裝完成後,您需要通過在 AddFluentValidation 方法在 Startup 類中的 ConfigureServices 方法中註冊服務。

public void ConfigureServices(IServiceCollection services)
{              
  services.AddMvc().AddFluentValidation().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); }

為了使驗證生效還需要再 ConfigureServices 中依賴注入我們的驗證器。

services.AddTransient<IValidator<Customer>, CustomerValidator>();

自動註冊驗證器

不過這樣一個個註冊太麻煩了,所以有一個批量註冊的方法 RegisterValidatorsFromAssemblyContaining 通過這個方法可以註冊特定程式集中的所有的驗證器。這將自動查詢從其繼承 AbstractValidator 並在容器中註冊的所有公共非抽象型別(不支援開放的泛型)。如下所示建立一個驗證器和一個驗證器介面,相應的類我就不建立了。

public class CustomerValidator : AbstractValidator<Customer> , IValidator
{
  public CustomerValidator()
  {
    RuleFor(t => t.Name).NotEmpty();
  }
}
public interface IValidator{}

之後在 Startup 類中的 ConfigureServices 方法中使用 RegisterValidatorsFromAssemblyContaining 註冊 IValidator 介面,這樣所有繼承 IValidator 和 AbstractValidator 的驗證器就會全部自動註冊了。

services.AddMvc().AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<IValidator>()).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

自動註冊的驗證器是以 Transient 的形式註冊的而不是 Singleton 。如果您不想註冊特定的驗證器型別,則可以使用過濾器回撥將其排除,如下所示就排除了CustomerValidator驗證器:

services.AddMvc().AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<IValidator>(discoveredType => discoveredType.ValidatorType != typeof(CustomerValidator))).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

在控制器中使用驗證器

直接用原來的 ModelState.IsValid 就可以了。

public IActionResult Create(Customer customer)
{
  customer = new Customer();
  CustomerValidator validationRules = new CustomerValidator();
  if (!ModelState.IsValid)
    return Content("失敗了");
  return View();
}

執行應用程式時,還可以選擇對子屬性啟用隱式驗證。啟用此功能後,無需使用來指定子驗證器 SetValidator,MVC的驗證基礎結構將遞迴地嘗試自動為每個屬性查詢驗證器。可以通過設定 ImplicitlyValidateChildProperties 為true來完成

services.AddMvc().AddFluentValidation(fv => {
 fv.ImplicitlyValidateChildProperties = true;
});

請注意,如果啟用此行為,則不應將其 SetValidator 用於子屬性,否則驗證程式將執行兩次。

手動驗證

有時您可能需要手動驗證 MVC 專案中的物件,在這種情況下, 我們可以將驗證結果複製到 MVC 的 ModelState 字典中,便可用於前端錯誤提示。

public ActionResult DoSomething() {
  var customer = new Customer();
  var validator = new CustomerValidator();
  var results = validator.Validate(customer);

  results.AddToModelState(ModelState, null);
  return View();
}

AddToModelState 方法是作為擴充套件方法實現的, 需要引用 FluentValidation 名稱空間,請注意, 第二個引數是可選的模型名稱字首, 該引數可設定物件屬性在 ModelState 字典中的字首。

驗證程式自定義

您可以使用 CustomizeValidatorAttribute 為模型指定驗證程式,也支援為驗證器指定規則集。

public ActionResult Save([CustomizeValidator(RuleSet="MyRuleset")] Customer cust) {
  // ...
}

這相當於為驗證指定規則集,等同於將規則集傳遞給驗證程式:

var validator = new CustomerValidator();
var customer = new Customer();
var result = validator.Validate(customer, ruleSet: "MyRuleset");

該屬性還可用於呼叫單個屬性的驗證:

public ActionResult Save([CustomizeValidator(Properties="Surname,Forename")] Customer cust) {
  // ...
}

這相當於對驗證程式指定特定屬性,其它屬性將不被驗證:

var validator = new CustomerValidator();
var customer = new Customer();
var result = validator.Validate(customer, properties: new[] { "Surname", "Forename" });

也可以使用 CustomizeValidatorAttribute 特性跳過某些型別的驗證。

public ActionResult Save([CustomizeValidator(Skip=true)] Customer cust) {
  // ...
}

驗證器攔截器

您可以使用攔截器進一步自定義驗證過程,攔截器必須實現 FluentValidation.Mvc 名稱空間中的 IValidatorInterceptor 介面:

public interface IValidatorInterceptor    {
  ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext);
  ValidationResult AfterMvcValidation(ControllerContext controllerContext, ValidationContext validationContext, ValidationResult result);
}

此介面有兩個方法:BeforeMvcValidation 和 AfterMvcValidation,分別可攔截驗證前和驗證後的過程。除了在驗證程式類中直接實現此介面外, 我們還可以在外部實現該介面, 通過 CustomizeValidatorAttribute 特性指定攔截器:

public ActionResult Save([CustomizeValidator(Interceptor=typeof(MyCustomerInterceptor))] Customer cust) {
 //...
}

在這種情況下, 攔截器必須是一個實現 IValidatorInterceptor 介面,並具有公共無引數建構函式的類。請注意, 攔截器是高階方案,大多數情況下, 您可能不需要使用攔截器, 但如果需要, 可以選擇它。

為客戶端指定規則集

預設情況下 FluentValidation 不會為客戶端生成基於規則集的驗證程式碼, 但您可以通過 RuleSetForClientSideMessagesAttribute 為客戶端指定規則集。

[RuleSetForClientSideMessages("MyRuleset")]
public ActionResult Index() {
   return View(new PersonViewModel());
}

也可以在控制器中使用 SetRulesetForClientsideMessages 擴充套件方法 (需要引用 FluentValidation 名稱空間)為客戶端指定規則集。

public ActionResult Index() {
   ControllerContext.SetRulesetForClientsideMessages("MyRuleset");
   return View(new PersonViewModel());
}

相關文章