FluentValidation 是一個基於 .NET 開發的驗證框架,開放原始碼且免費,而且優雅,支援鏈式操作,易於理解,功能完善,還可與 MVC5、WebApi2 和 ASP.NET CORE 深度整合,元件內提供十幾種常用驗證器,可擴充性好,支援自訂驗證器,支援本地化多語言。
雖然 FluentValidation 是一個非常強大的驗證框架,但針對該框架的中文資料並不完善,零度在學習的過程中,將官方文件進行了翻譯,由此產生本文,可供參閱。
要使用驗證框架,需要在專案中加入對 FluentValidation.dll 的參考,支援 netstandard2.0 程式庫和 .NET4.5 平台,支援 .NET Core 平台,最簡單的方法是使用 NuGet 套件管理器參考元件。
Install-Package FluentValidation
若要在 ASP.NET Core 中使用 FluentValidation 擴充,可參考以下套件:
Install-Package FluentValidation.AspNetCore
若要在 ASP.NET MVC 5 或 WebApi 2 專案整合,可以使用分別使用 FluentValidation.Mvc5 和 FluentValidation.WebApi 套件。
Install-Package FluentValidation.Mvc5
Install-Package FluentValidation.WebApi
建立第一個驗證程式
若要為特定物件定義一組驗證規則,您需要建立一個從 AbstractValidator<T> 繼承的類別,其中泛型 T 參數是要驗證的類別的型別。假設您有一個客戶類別:
public class Customer {
public int Id { get; set; }
public string Surname { get; set; }
public string Forename { get; set; }
public decimal Discount { get; set; }
public string Address { get; set; }
}
接下來自訂繼承於 AbstractValidator 泛型類別的驗證器,然後在建構函式中使用 LINQ 運算式編寫 RuleFor 驗證規則。
using FluentValidation;
public class CustomerValidator : AbstractValidator<Customer> {
public CustomerValidator() {
RuleFor(customer => customer.Surname).NotNull();
}
}
若要執行驗證程式,我們透過定義好的 CustomerValidator 驗證器傳入實體類別 Customer 即可。
Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
ValidationResult result = validator.Validate(customer);
該驗證方法回傳一個 ValidationResult 物件,表示驗證結果,ValidationResult 包含兩個屬性:IsValid 屬性是布林值,它表示驗證是否成功,Errors 屬性包含驗證失敗的詳細資訊。
下面的程式碼示範向主控台輸出驗證失敗的詳細資訊:
Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
ValidationResult results = validator.Validate(customer);
if(! results.IsValid) {
foreach(var failure in results.Errors) {
Console.WriteLine("Property " + failure.PropertyName + " Error was: " + failure.ErrorMessage);
}
}
鏈結規則寫法
您可以將物件的同一屬性用多個驗證程式鏈在一起,以下程式碼將驗證 Surname 屬性不為 Null 的同時且不等於 foo 字串。
using FluentValidation;
public class CustomerValidator : AbstractValidator<Customer> {
public CustomerValidator() {
RuleFor(customer => customer.Surname).NotNull().NotEqual("foo");
}
}
引發例外
如果驗證失敗,不想回傳 ValidationResult 物件,而是想直接擲回例外,可透過呼叫驗證器的 ValidateAndThrow 進行驗證。
Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
validator.ValidateAndThrow(customer);
如果驗證失敗,將引發一個 ValidationException 型別的例外,這個例外可以被上層程式捕捉,並從例外中找到詳細錯誤資訊。
集合
當針對一個集合進行驗證時,只需要定義集合項目標型的規則即可,以下規則將對集合中的每個元素執行 NotNull 檢查。
public class Person {
public List<string> AddressLines {get;set;} = new List<string>();
}
public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
RuleForEach(x => x.AddressLines).NotNull();
}
}
複雜屬性
驗證程式可以用於複雜屬性,假設您有兩個類別:客戶和地址。
public class Customer {
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address {
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string Postcode { get; set; }
}
然後定義一個基於地址的 AddressValidator 驗證器件:
public class AddressValidator : AbstractValidator<Address> {
public AddressValidator() {
RuleFor(address => address.Postcode).NotNull();
//etc
}
}
然後定義一個基於客戶的 CustomerValidator 驗證器件,對地址驗證時使用地址驗證器。
public class CustomerValidator : AbstractValidator<Customer> {
public CustomerValidator() {
RuleFor(customer => customer.Name).NotNull();
RuleFor(customer => customer.Address).SetValidator(new AddressValidator());
}
}
另外,還可以在集合屬性上使用驗證程式,假設客戶物件包含訂單集合屬性:
public class Customer {
public IList<Order> Orders { get; set; }
}
public class Order {
public string ProductName { get; set; }
public decimal? Cost { get; set; }
}
var customer = new Customer();
customer.Orders = new List<Order> {
new Order { ProductName = "Foo" },
new Order { Cost = 5 }
};
定義了一個 OrderValidator 驗證器件:
public class OrderValidator : AbstractValidator<Order> {
public OrderValidator() {
RuleFor(x => x.ProductName).NotNull();
RuleFor(x => x.Cost).GreaterThan(0);
}
}
此驗證程式可在 CustomerValidator 中透過 SetCollectionValidator 方法使用:
public class CustomerValidator : AbstractValidator<Customer> {
public CustomerValidator() {
RuleFor(x => x.Orders).SetCollectionValidator(new OrderValidator());
}
}
var validator = new CustomerValidator();
var results = validator.Validate(customer);
執行行驗證程式時,透過驗證結果,可以輸出訂單每個屬性驗證錯誤的詳細資訊:
foreach(var result in results.Errors) {
Console.WriteLine("Property name: " + result.PropertyName);
Console.WriteLine("Error: " + result.ErrorMessage);
Console.WriteLine("");
}
Property name: Orders[0].Cost
Error: 'Cost' must be greater than '0'.
Property name: Orders[1].ProductName
Error: 'Product Name' must not be empty.
在編寫驗證規則時,可以透過 Where 關鍵字排除或者篩選不需要驗證的物件。
RuleFor(x => x.Orders).SetCollectionValidator(new OrderValidator())
.Where(x => x.Cost != null);
支援規則集
規則集允許您將驗證規則組合在一起,作為一個組一起執行,同時忽略其他規則,假如:Person 類別有 3 個屬性分別是 Id、Surname 和 Forename,我們將 Id 單獨驗證, Surname 和 Forename 作為一組 Names 規則集進行驗證。
public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
RuleSet("Names", () => {
RuleFor(x => x.Surname).NotNull();
RuleFor(x => x.Forename).NotNull();
});
RuleFor(x => x.Id).NotEqual(0);
}
}
然後,我們可以使用驗證器提供的擴充方法,針對指定的規則集執行驗證,以下程式碼將不驗證 Id 屬性。
var validator = new PersonValidator();
var person = new Person();
var result = validator.Validate(person, ruleSet: "Names");
執行多個規則集驗證,可以使用逗號分隔的字串列表。
validator.Validate(person, ruleSet: "Names,MyRuleSet,SomeOtherRuleSet")
透過*號匹配所有規則,可以強制執行所有規則,不管它們是否在規則集中:
validator.Validate(person, ruleSet: "*")
設定方法
透過在驗證程式上呼叫 WithMessage 方法,可以覆蓋驗證程式的預設驗證錯誤訊息:
RuleFor(customer => customer.Surname).NotNull().WithMessage("姓名不能為空");
錯誤提示中,可以透過 佔位符取代屬性名:
RuleFor(customer => customer.Surname).NotNull().WithMessage("{PropertyName}不能為空");
除了 佔位符,框架還內建了:、、、和 佔位符,關於更多內建佔位符,可以參閱官方文件。
預設情況下,錯誤訊息會輸出屬性名稱,以下規則驗證錯誤時,輸出 Surname 不能為空。
RuleFor(customer => customer.Surname).NotNull();
驗證程式支援透過 WithName 方法來指定屬性別名,以下程式碼輸出姓名不能為空。
RuleFor(customer => customer.Surname).NotNull().WithName("姓名");
FluentValidation 還支援透過 DisplayNameResolver 自訂別名取得邏輯:
ValidatorOptions.DisplayNameResolver = (type, member) => {
if(member != null) {
return member.Name + "Foo";
}
return null;
};
也可以使用 DisplayName 特性指定屬性別名。
public class Person {
[Display(Name="Last name")]
public string Surname { get; set; }
}
某些條件下,可透過 When 方法按條件設定驗證規則,如下程式碼:GreaterThan 規則僅在 IsPreferredCustomer 為 true 時才執行。
RuleFor(customer => customer.CustomerDiscount).GreaterThan(0).When(customer => customer.IsPreferredCustomer);
如果需要為多個規則指定相同的條件,則可以在方法頂層呼叫,而不是在規則結束時將呼叫:
When(customer => customer.IsPreferred, () => {
RuleFor(customer => customer.CustomerDiscount).GreaterThan(0);
RuleFor(customer => customer.CreditCardNumber).NotNull();
});
預設情況,編寫多個鏈式驗證規則時,下無論前一個規則失敗與否,後一個規則都將執行,以下程式碼檢查 Surname 是否為空,然後檢查 Surname 不等於零度,如多 NotNull 驗證失敗,則仍將呼叫 NotEqual 驗證。
RuleFor(x => x.Surname).NotNull().NotEqual("零度");
我們可以透過 StopOnFirstFailure 方法制定級聯模式。
RuleFor(x => x.Surname).Cascade(CascadeMode.StopOnFirstFailure).NotNull().NotEqual("foo");
現在,如果 NotNull 驗證程式失敗,NotEqual 驗證程式將不會執行。
CascadeMode 是一個列舉,Continue 表示始終呼叫規則定義中的所有驗證程式,StopOnFirstFailure 表示一旦驗證程式失敗,就停止執行後邊的規則。
若要全域設定級聯模式,可以在應用程式啟動時修改 ValidatorOptions 型別的 CascadeMode 屬性:
ValidatorOptions.CascadeMode = CascadeMode.StopOnFirstFailure;
若要為單個驗證程式類別設定級聯模式,可以這樣寫:
public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
// First set the cascade mode
CascadeMode = CascadeMode.StopOnFirstFailure;
// Rule definitions follow
RuleFor(...)
RuleFor(...)
}
}
預設情況下,FluentValidation 中的所有規則都是獨立執行的,無相互影響,有助於非同步驗證提高效能,但是,有些情況下,您希望某些規則僅在另一個規則驗證完成後執行,透過 DependentRules 指定規則依賴,當依賴規則驗證完成時,當前規則則會執行。
RuleFor(x => x.Surname).NotNull().DependentRules(() => {
RuleFor(x => x.Forename).NotNull();
});
我們可以透過 ValidationContext 上下文的 RootContextData 字典向驗證程式傳遞資料。
var instanceToValidate = new Person();
var context = new ValidationContext(person);
context.RootContextData["MyCustomData"] = "Test";
var validator = new PersonValidator();
validator.Validate(context);
然後,可以透過呼叫 Custom 方法在任何自訂屬性驗證程式內存取 RootContextData 字典。
RuleFor(x => x.Surname).Custom((x, context) => {
if(context.ParentContext.RootContextData.ContainsKey("MyCustomData")) {
context.AddFailure("My error message");
}
});
如果每次呼叫驗證程式前,都需要執行特定的程式碼,可以透過覆寫 PreValidate 方法來攔截驗證程式,該方法回傳 true 驗證程式將繼續,回傳 false 將終止繼續驗證。
public class MyValidator : AbstractValidator<Person> {
public MyValidator() {
RuleFor(x => x.Name).NotNull();
}
protected override bool PreValidate(ValidationContext<Person> context, ValidationResult result) {
if (context.InstanceToValidate == null) {
result.Errors.Add(new ValidationFailure("", "Please ensure a model was supplied."));
return false;
}
return true;
}
}
內建驗證程式
FluentValidation 有幾個內建的驗證器,這些驗證器的錯誤訊息都可以使用特定佔位符。
NotNull 驗證程式
說明:確保指定的屬性不是 null。
RuleFor(customer => customer.Surname).NotNull();
可用的格式參數佔位符: = 正在驗證的屬性的名稱 = 屬性的當前值
NotEmpty 驗證程式
說明:確保指定的屬性不是 null、空字串或空格 (或值型別的預設值,例如 int 0)。
RuleFor(customer => customer.Surname).NotEmpty();
可用的格式參數佔位符: = 正在驗證的屬性的名稱 = 屬性的當前值
NotEqual 驗證程式
說明:確保指定屬性的值不等於特定值 (或不等於其他屬性的值)
//Not equal to a particular value
RuleFor(customer => customer.Surname).NotEqual("Foo");
//Not equal to another property
RuleFor(customer => customer.Surname).NotEqual(customer => customer.Forename);
可用的格式參數佔位符: = 正在驗證的屬性的名稱 = 屬性不應等於的值
Equal 相等驗證程式
說明:確保指定屬性的值等於特定值 (或等於另一個屬性的值)
//Equal to a particular value
RuleFor(customer => customer.Surname).Equal("Foo");
//Equal to another property
RuleFor(customer => customer.Password).Equal(customer => customer.PasswordConfirmation);
可用的格式參數佔位符: = 正在驗證的屬性的名稱 = 屬性應相等的值 = 屬性的當前值
Length 長度驗證程式
確保特定字串屬性的長度位於指定範圍內。但是,它不能確保字串屬性是否為 null。
RuleFor(customer => customer.Surname).Length(1, 250); //must be between 1 and 250 chars (inclusive)
可用的格式參數佔位符:
= 正在驗證的屬性的名稱 = 最小長度 = 最大長度 = 輸入的字元數 = 屬性的當前值
MaxLength 最大長度驗證程式
說明:確保特定字串屬性的長度不超過指定的值。
RuleFor(customer => customer.Surname).MaximumLength(250); //must be 250 chars or fewer
可用的格式參數佔位符: = 正在驗證的屬性的名稱 = 最大長度 = 輸入的字元數 = 屬性的當前值
MinLength 最小長度驗證程式
說明:確保特定字串屬性的長度不能小於指定的值。
RuleFor(customer => customer.Surname).MinimumLength(10); //must be 10 chars or more
可用的格式參數佔位符: = 正在驗證的屬性的名稱 = 最小長度 = 輸入的字元數 = 屬性的當前值
LessThan 小於驗證程式
說明:確保指定屬性的值小於特定值 (或小於另一個屬性的值)
//Less than a particular value
RuleFor(customer => customer.CreditLimit).LessThan(100);
//Less than another property
RuleFor(customer => customer.CreditLimit).LessThan(customer => customer.MaxCreditLimit);
可用的格式參數佔位符: = 正在驗證的屬性的名稱 -屬性比較的值 = 屬性的當前值
LessThanOrEqualTo 小於等於驗證程式
說明:確保指定屬性的值小於等於特定值 (或小於等於另一個屬性的值)
//Less than a particular value
RuleFor(customer => customer.CreditLimit).LessThanOrEqualTo(100);
//Less than another property
RuleFor(customer => customer.CreditLimit).LessThanOrEqualTo(customer => customer.MaxCreditLimit);
可用的格式參數佔位符: = 正在驗證的屬性的名稱 -屬性比較的值 = 屬性的當前值
GreaterThan 大於驗證程式
說明:確保指定屬性的值大於特定值 (或大於另一個屬性的值)
//Greater than a particular value
RuleFor(customer => customer.CreditLimit).GreaterThan(0);
//Greater than another property
RuleFor(customer => customer.CreditLimit).GreaterThan(customer => customer.MinimumCreditLimit);
可用的格式參數佔位符: = 正在驗證的屬性的名稱 -屬性比較的值 = 屬性的當前值
GreaterThanOrEqualTo 大於等於驗證程式
說明:確保指定屬性的值大於等於特定值 (或大於等於另一個屬性的值)
//Greater than a particular value
RuleFor(customer => customer.CreditLimit).GreaterThanOrEqualTo(1);
//Greater than another property
RuleFor(customer => customer.CreditLimit).GreaterThanOrEqualTo(customer => customer.MinimumCreditLimit);
可用的格式參數佔位符: = 正在驗證的屬性的名稱 -屬性比較的值 = 屬性的當前值
Must 驗證程式
描述:將指定屬性的值傳遞到一個委派中,可以對該值執行自訂驗證邏輯
RuleFor(customer => customer.Surname).Must(surname => surname == "Foo");
可用的格式參數佔位符: = 正在驗證的屬性的名稱 = 屬性的當前值
請注意,委派參數不僅傳遞參數,還支援直接傳遞驗證物件參數:
RuleFor(customer => customer.Surname).Must((customer, surname) => surname != customer.Forename)
正則表達式驗證程式
說明:確保指定屬性的值與給定的正則表達式匹配,正則表達式可參閱正則表達式教學(站長註:連結已失效)這篇文章
RuleFor(customer => customer.Surname).Matches("some regex here");
可用的格式參數佔位符: = 正在驗證的屬性的名稱 = 屬性的當前值
Email 電子郵件驗證程式
說明:確保指定屬性的值是有效的電子郵件地址格式。
RuleFor(customer => customer.Email).EmailAddress();
可用的格式參數佔位符: = 正在驗證的屬性的名稱 = 屬性的當前值
自訂驗證程式
Must 驗證程式
實作自訂驗證程式的最簡單方法是使用方法 Must 方法,假設我們有以下類別:
public class Person {
public IList<Person> Pets {get;set;} = new List<Person>();
}
為了確保列表中至少包含 10 個元素,我們可以這樣做:
public class PersonValidator:AbstractValidator<Person> {
public PersonValidator() {
RuleFor(x => x.Pets).Must(list => list.Count <= 10).WithMessage("The list must contain fewer than 10 items");
}
}
為了使這種邏輯可重複使用,我們可以將其封裝為擴充方法。
public static class MyCustomValidators {
public static IRuleBuilderOptions<T, IList<TElement>> ListMustContainFewerThan<T, TElement>(this IRuleBuilder<T, IList<TElement>> ruleBuilder, int num) {
return ruleBuilder.Must(list => list.Count < num).WithMessage("The list contains too many items");
}
}
在這裡,我們透過為 IRuleBuilder 建立擴充方法實現可重複使用邏輯,使用方法很簡單。
RuleFor(x => x.Pets).ListMustContainFewerThan(10);
編寫自訂驗證程式
如果您想靈活控制可重複使用的驗證器,則可以使用 Must 方法編寫自訂規則,此方法允許您手動建立與驗證錯誤關聯的實例。
public class PersonValidator:AbstractValidator<Person> {
public PersonValidator() {
RuleFor(x => x.Pets).Custom((list, context) => {
if(list.Count > 10) {
context.AddFailure("The list must contain 10 items or fewer");
}
});
}
}
此方法的優點是它允許您為同一規則回傳多個錯誤。
context.AddFailure("SomeOtherProperty", "The list must contain 10 items or fewer");
// Or you can instantiate the ValidationFailure directly:
context.AddFailure(new ValidationFailure("SomeOtherProperty", "The list must contain 10 items or fewer");
自訂屬性驗證程式
在某些情況下,針對某些屬性的驗證邏輯非常複雜,我們希望將基於屬性的自訂邏輯移動到單獨的類別中,可透過覆寫 PropertyValidator 類別來完成。
using System.Collections.Generic;
using FluentValidation.Validators;
public class ListCountValidator<T> : PropertyValidator {
private int _max;
public ListCountValidator(int max)
: base("{PropertyName} must contain fewer than {MaxElements} items.") {
_max = max;
}
protected override bool IsValid(PropertyValidatorContext context) {
var list = context.PropertyValue as IList<T>;
if(list != null && list.Count >= _max) {
context.MessageFormatter.AppendArgument("MaxElements", _max);
return false;
}
return true;
}
}
繼承 PropertyValidator 時,必須覆寫 IsValid 方法,此方法接受一個物件,並回傳一個布林值,指示驗證是否成功,可透過 PropertyValidatorContext 屬性存取: Instance-正在驗證的物件 PropertyDescription-屬性的名稱 (或者是由呼叫 WithName 的自訂的別名) PropertyValue-正在驗證的屬性值 Member-描述正在驗證的屬性的 MemberInfo 資訊
若要使用自訂的屬性驗證程式,可以在定義驗證規則時呼叫:
public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
RuleFor(person => person.Pets).SetValidator(new ListCountValidator<Pet>(10));
}
}
本地化與多語言
FluentValidation 為預設驗證訊息提供幾種語言的翻譯,預設情況下,會根據目前執行緒的 CurrentUICulture 語言文化來選擇語言,你也可以使用 WithMessage 和 WithLocalizedMessage 來指定錯誤提示。
WithMessage
如果使用 Visual Studio 的內建的 resx 格式資源檔,則可以透過呼叫 WithMessage 本地化錯誤訊息。
RuleFor(x => x.Surname).NotNull().WithMessage(x => MyLocalizedMessages.SurnameRequired);
當然,您可以將多種語言儲存在資料庫中,透過 lambda 運算式來讀取多語言訊息。
WithLocalizedMessage
您可以透過呼叫 WithLocalizedMessage 方法,傳遞資源類型和資源名稱,使錯誤訊息支援本地多語言。
RuleFor(x => x.Surname).NotNull().WithLocalizedMessage(typeof(MyLocalizedMessages), "SurnameRequired");
預設錯誤訊息
如果您要取代 FluentValidation 的預設錯誤提示,可以實作 ILanguageManager 介面,該介面支援本地化多語言。
public class CustomLanguageManager : FluentValidation.Resources.LanguageManager {
public CustomLanguageManager() {
AddTranslation("en", "NotNullValidator", "'{PropertyName}' is required.");
}
}
以上程式碼為 NotNullValidator 驗證器自訂英文錯誤提示訊息,當然也可以實現更多的語言,定義好 LanguageManager 後,需要在啟動時進行設定。
ValidatorOptions.LanguageManager = new CustomLanguageManager();
停用本地化多語言
您可以完全停用 FluentValidation 對本地化的支援,這將強制使用預設的英文語言,而不考慮執行緒的 CurrentUICulture。這可以透過靜態類別 ValidatorOptions 在應用程式的啟動常式中完成。
ValidatorOptions.LanguageManager.Enabled = false;
還可以強制指定預設語言,始終以特定語言顯示:
ValidatorOptions.LanguageManager.Culture = new CultureInfo("fr");
ASP.NET Core 整合
FluentValidation 可以與 ASP.NET Core 整合,要啟用 MVC 整合,您需要透過 NuGet 套件來加入對組件 FluentValidation.AspNetCore 的參考:
Install-Package FluentValidation.AspNetCore
安裝後,您需要參考命名空間 using FluentValidation.AspNetCore,然後,在應用程式的啟動類別 Startup 中透過呼叫 services 的擴充方法 AddFluentValidation 進行設定。
public void ConfigureServices(IServiceCollection services) {
services.AddMvc(setup => {
//...mvc setup...
}).AddFluentValidation();
}
為了讓 ASP.NET Core 發現您的驗證程式,必須在服務集容器中註冊它們。
public void ConfigureServices(IServiceCollection services) {
services.AddMvc(setup => {
//...mvc setup...
}).AddFluentValidation();
services.AddTransient<IValidator<Person>, PersonValidator>();
//etc
}
或使用 AddFromAssemblyContaining 方法自動註冊特定組件中的所有驗證器。
services.AddMvc()
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<PersonValidator>());
本範例定義 PersonValidator 驗證器,用於驗證 Person 型別。
public class Person {
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
RuleFor(x => x.Id).NotNull();
RuleFor(x => x.Name).Length(0, 10);
RuleFor(x => x.Email).EmailAddress();
RuleFor(x => x.Age).InclusiveBetween(18, 60);
}
}
定義控制器 PeopleController 用於建立並儲存 Person 實例,使用 ModelState.IsValid 判斷使用者的輸入是否驗證通過。
public class PeopleController : Controller {
public ActionResult Create() {
return View();
}
[HttpPost]
public IActionResult Create(Person person) {
if(! ModelState.IsValid) { // re-render the view when validation failed.
return View("Create", person);
}
Save(person); //Save the person to the database, or some other logic
TempData["notice"] = "Person successfully created";
return RedirectToAction("Index");
}
}
這是我們建立 Person 的檢視程式碼:
@model Person
<div asp-validation-summary="ModelOnly"></div>
<form asp-action="Create">
Id: <input asp-for="Id" /> <span asp-validation-for="Id"></span>
<br />
Name: <input asp-for="Name" /> <span asp-validation-for="Name"></span>
<br />
Email: <input asp-for="Email" /> <span asp-validation-for="Email"></span>
<br />
Age: <input asp-for="Age" /> <span asp-validation-for="Age"></span>
<br /><br />
<input type="submit" value="submtit" />
</form>
現在,當您提交表單時,MVC 的模型繫結系統將使用 PersonPersonValidator 驗證輸入,並將驗證結果儲存至 ModelState 用於判斷。
與 ASP.NET Core 內建驗證的相容性
預設情況下,在執行 FluentValidation 驗證之後,MVC 原生內建的 DataAnnotations 驗證方式也可能執行,這意味著您可以將 FluentValidation 與 DataAnnotations 驗證混合在一起使用。如果要停用此行為,將 FluentValidation 設為唯一的驗證程式庫,可以將 RunDefaultMvcValidationAfterFluentValidationExecutes 屬性設為 false 值:
services.AddMvc().AddFluentValidation(fv => {
fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false;
});
隱含與顯式子屬性驗證
在驗證複雜物件時,預設情況下,您必須手動透過 SetValidator 方法指定複雜屬性的子驗證器。執行 ASP.NET MVC 應用程式時,還可以選擇為子屬性啟用隱含驗證,啟用此功能時,MVC 的驗證基礎結構將會自動尋找每個屬性的驗證程式,而不必指定的子驗證程式,這可以透過將 SetValidatorImplicitlyValidateChildProperties 設定為 true 來完成:
services.AddMvc().AddFluentValidation(fv => {
fv.ImplicitlyValidateChildProperties = true;
});
請注意,如果啟用此行為,則不應手動透過 SetValidator 指定子屬性驗證程式,否則,驗證程式將執行兩次。
用戶端驗證
FluentValidation 是伺服器端驗證框架,不直接提供任何用戶端驗證。但是,它可以產生 jQuery 驗證框架支援的 HTML 元素元資料,用於支援 jquery.validate 框架的自動驗證。
手動驗證
有時您可能需要手動驗證 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());
}
ASP.NET MVC 5 整合
FluentValidation 還可以與舊的 ASP.NET MVC 5 專案整合,需要透過 NuGet 加入對組件 FluentValidation.Mvc5 的參考,然後參考 FluentValidation.Mvc 命名空間:
Install-Package FluentValidation.Mvc5
安裝後,需要在應用程式全域檔案 (Global.asax) 中設定 Application_Start 事件,以便於 FluentValidation 執行初始化。
protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
FluentValidationModelValidatorProvider.Configure();
}
在 MVC 內部,FluentValidation 利用驗證器工廠為特定型別建立驗證程式,預設情況下使用 AttributedValidatorFactory 工廠,透過特性的方式,允許您為指定的模型設定驗證程式:
[Validator(typeof(PersonValidator))]
public class Person {
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
public class PersonValidator : AbstractValidator<Person> {
public PersonValidator() {
RuleFor(x => x.Id).NotNull();
RuleFor(x => x.Name).Length(0, 10);
RuleFor(x => x.Email).EmailAddress();
RuleFor(x => x.Age).InclusiveBetween(18, 60);
}
}
您還可以使用 MVC 自帶的 IoC 相依性插入容器實現自訂的驗證器工廠,而不是使用上述特性標記的方法。
FluentValidationModelValidatorProvider.Configure(provider => {
provider.ValidatorFactory = new MyCustomValidatorFactory();
});
最後,我們可以建立控制器和與之關聯的檢視:
public class PeopleController : Controller {
public ActionResult Create() {
return View();
}
[HttpPost]
public ActionResult Create(Person person) {
if(! ModelState.IsValid) { // re-render the view when validation failed.
return View("Create", person);
}
TempData["notice"] = "Person successfully created";
return RedirectToAction("Index");
}
}
這是相應的檢視程式碼:
@Html.ValidationSummary() @using (Html.BeginForm()) { Id: @Html.TextBoxFor(x =>
x.Id) @Html.ValidationMessageFor(x => x.Id)
<br />
Name: @Html.TextBoxFor(x => x.Name) @Html.ValidationMessageFor(x => x.Name)
<br />
Email: @Html.TextBoxFor(x => x.Email) @Html.ValidationMessageFor(x => x.Email)
<br />
Age: @Html.TextBoxFor(x => x.Age) @Html.ValidationMessageFor(x => x.Age)
<br /><br />
<input type="submit" value="submit" />
}
現在,當您提交表單時,MVC 將使用 FluentValidation 框架驗證模型。
特別說明
在 ASP.NET MVC 5 中使用 FluentValidation 框架,和 ASP.NET Core 基本相同,所以,用戶端驗證、手動驗證、驗證程式自訂、驗證器攔截器和用戶端規則集都類似於上文,由於文章篇幅,這裡將不再贅述。
ASP.NET WebApi 2 整合
FluentValidation 的 WebApi 的整合與 MVC 5 整合 (上面) 相同,但您需要透過 NuGet 參考 FluentValidation.WebApi 套件。
出處:https://www.xcode.me/post/5849(站長註:該連結已失敗,可在部落格園瀏覽轉載文章:https://www.cnblogs.com/mq0036/p/14548370.html)