FluentValidation:C#后端輸入驗證框架的官方文檔解讀


參照 FluentValidation 的官方文檔寫的例子,方便日后查看和使用。

原文:https://github.com/JeremySkinner/FluentValidation/wiki

 Home

NuGet Packages

Install-Package FluentValidation

For ASP.NET MVC integration:

Install-Package FluentValidation.MVC5

For ASP.NET Core:

Install-Package FluentValidation.AspNetCore

Example

using FluentValidation;

public class CustomerValidator: AbstractValidator<Customer> {
  public CustomerValidator() {
    RuleFor(customer => customer.Surname).NotEmpty();
    RuleFor(customer => customer.Forename).NotEmpty().WithMessage("Please specify a first name");
    RuleFor(customer => customer.Discount).NotEqual(0).When(customer => customer.HasDiscount);
    RuleFor(customer => customer.Address).Length(20, 250);
    RuleFor(customer => customer.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode");
  }

  private bool BeAValidPostcode(string postcode) {
    // custom postcode validating logic goes here
  }
}

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
ValidationResult results = validator.Validate(customer);

bool validationSucceeded = results.IsValid;
IList<ValidationFailure> failures = results.Errors;

a. Index

Documentation table of contents

b. Creating a Validator

定義對象,及對象驗證類

    public class Customer
    {
        public int Id { get; set; }
        public string Surname { get; set; }
        public string Forename { get; set; }
        public decimal Discount { get; set; }
        public Address Address { get; set; }
        public List<string> AddressLines { get; set; } = new List<string>();
        public IList<Order> Orders { get; set; }
    }

    /// <summary>
    /// 為Customer類定義一組規則,繼承自AbstractValidator<Customer>
    /// </summary>
    public class CustomerValidator : AbstractValidator<Customer>
    {
        public CustomerValidator()
        {
            //多個驗證規則
            RuleFor(customer => customer.Surname).NotNull().NotEqual("foo");

            //對集合中的每一項進行驗證
            RuleForEach(x => x.AddressLines).NotNull();

            //復合屬性驗證的重新利用
            RuleFor(customer => customer.Address).SetValidator(new AddressValidator());

            //復合的集合屬性驗證的重新利用
            RuleFor(x => x.Orders).SetCollectionValidator(new OrderValidator());
            //用Where方法選擇性驗證某些項
            RuleFor(x => x.Orders).SetCollectionValidator(new OrderValidator()).Where(x => x.Cost != null);

            //定義名為“Names”的規則集合
            RuleSet("Names", () =>
            {
                RuleFor(x => x.Surname).NotNull();
                RuleFor(x => x.Forename).NotNull();
            });
        }
    }
    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; }
    }
    public class AddressValidator : AbstractValidator<Address>
    {
        public AddressValidator()
        {
            RuleFor(address => address.Postcode).NotNull();
            //etc
        }
    }
    public class Order
    {
        public string ProductName { get; set; }
        public decimal? Cost { get; set; }
    }
    public class OrderValidator : AbstractValidator<Order>
    {
        public OrderValidator()
        {
            RuleFor(x => x.ProductName).NotNull();
            RuleFor(x => x.Cost).GreaterThan(0);
        }
    }

調用

        public void Validate()
        {
            Customer customer = new Customer();
            customer.Orders = new List<Order> {
                new Order { ProductName = "Foo" },
                new Order { Cost = 5 }
            };

            CustomerValidator validator = new CustomerValidator();
            ValidationResult results = validator.Validate(customer);

            //將復雜的驗證定義分解到較小的片段中,可獨立使用
            //把多個規則聚集都一個規則集合中
            var result1 = validator.Validate(customer, ruleSet: "Names");
            //多個規則集合
            var result2 = validator.Validate(customer, ruleSet: "Names,MyRuleSet,SomeOtherRuleSet");
            //不在任何規則集合中的default
            var result3 = validator.Validate(customer, ruleSet: "default,MyRuleSet");

            if (!results.IsValid)
            {
                foreach (var failure in results.Errors)
                {
                    Console.WriteLine("Property " + failure.PropertyName + " failed validation. Error was: " + failure.ErrorMessage);
                }
            }

            //拋出異常
            validator.ValidateAndThrow(customer);

        }

 

c. Built In Validators 內置驗證器 

//Ensures that the specified property is not null.
RuleFor(customer => customer.Surname).NotNull();

//Ensures that the specified property is not null, an empty string or whitespace (or the default value for value types, eg 0 for int)
RuleFor(customer => customer.Surname).NotEmpty();

//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 to a particular value
RuleFor(customer => customer.Surname).Equal("Foo");
//Equal to another property
RuleFor(customer => customer.Password).Equal(customer => customer.PasswordConfirmation);

//must be between 1 and 250 chars (inclusive)
RuleFor(customer => customer.Surname).Length(1, 250);

//Less than a particular value
RuleFor(customer => customer.CreditLimit).LessThan(100);
//Less than another property
RuleFor(customer => customer.CreditLimit).LessThan(customer => customer.MaxCreditLimit);

//Less than a particular value
RuleFor(customer => customer.CreditLimit).LessThanOrEqualTo(100);
//Less than another property
RuleFor(customer => customer.CreditLimit).LessThanOrEqualTo(customer => customer.MaxCreditLimit);

//Greater than a particular value
RuleFor(customer => customer.CreditLimit).GreaterThan(0);
//Greater than another property
RuleFor(customer => customer.CreditLimit).GreaterThan(customer => customer.MinimumCreditLimit);

//Greater than a particular value
RuleFor(customer => customer.CreditLimit).GreaterThanOrEqualTo(1);
//Greater than another property
RuleFor(customer => customer.CreditLimit).GreaterThanOrEqualTo(customer => customer.MinimumCreditLimit);

//Passes the value of the specified property into a delegate that can perform custom validation logic on the value
//Also known as "Must"
RuleFor(customer => customer.Surname).Must(surname => surname == "Foo");
//Note that there is an additional overload for Must that also accepts an instance of the parent object being validated. 
//This can be useful if you want to compare the current property with another property from inside the predicate:
//Note that in this particular example, it would be better to use the cross-property version of NotEqual
RuleFor(customer => customer.Surname).Must((customer, surname) => surname != customer.Forename);

//Ensures that the value of the specified property matches the given regular expression. Example:
RuleFor(customer => customer.Surname).Matches("some regex here");

//Ensures that the value of the specified property is a valid email address format. Example:
RuleFor(customer => customer.Email).EmailAddress();

d. Configuring a Validator 配置驗證器

1.Override the default error message

RuleFor(customer => customer.Surname).NotNull().WithMessage("Please ensure that you have entered your Surname");
//'{PropertyName}' will be replaced with the name of the property being validated
//and the value 'Surname' will be inserted.
RuleFor(customer => customer.Surname).NotNull().WithMessage("Please ensure you have entered your {PropertyName}");
Configuring Error Message Parameters (Placeholders)
 
The placeholders are Used in all validators:
'{PropertyName}' - The name of the property being validated
'{PropertyValue}' - The value of the property being validated These include the predicate validator('Must' validator), the email and the regex validators.
 
Used in comparison validators: (Equal, NotEqual, GreaterThan, GreaterThanOrEqual, etc.)
{ ComparisonValue} = Value that the property should be compared to
 
Used only in the Length validator:
{ MinLength} = Minimum length
{ MaxLength} = Maximum length
{ TotalLength} = Number of characters entered
//Using static values in a custom message:
RuleFor(customer => customer.Surname).NotNull()
    .WithMessage(customer => string.Format("This message references some constant values: {0} {1}", "hello", 5));
//Result would be "This message references some constant values: hello 5"

//Referencing other property values:
RuleFor(customer => customer.Surname).NotNull()
    .WithMessage(customer => $"This message references some other properties: Forename: {customer.Forename} Discount: {customer.Discount}");
//Result would be: "This message references some other properties: Forename: Jeremy Discount: 100"

2.Overriding the Default Property Name

//The default error message would be 'Surname' must not be empty. 
RuleFor(customer => customer.Surname).NotNull();
//Replace just the property name by calling WithName //Now the error message would be 'Last name' must not be empty. RuleFor(customer => customer.Surname).NotNull().WithName("Last name");
//Completely rename the property, including the Errors collection on the ValidationResult RuleFor(customer => customer.Surname).NotNull().OverridePropertyName("Last name");
//Property name resolution is also pluggable.By default, the name of the property extracted from the MemberExpression passed to RuleFor. If you want change this logic, you can set the DisplayNameResolver property on the ValidatorOptions class.
//這段不是太懂
ValidatorOptions.DisplayNameResolver = (type, member) => {
    if (member != null)
    {
        return member.Name + "Foo";
    }
    return null;
};
//另外,FluentValidation 可采用 DisplayName and Display attributes,來生成錯誤信息中的屬性名,同 WithName
public class Person
{
    [Display(Name = "Last name")]
    public string Surname { get; set; }
}

 

3.Specifying a condition with When/Unless 指定條件

//當滿足條件時才執行驗證規則(Unless則相反)
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();
});

4.Setting the Cascade mode 設置級聯模式

//默認是第一個驗證失敗后,繼續執行第二個驗證
RuleFor(x => x.Surname).NotNull().NotEqual("foo");
//第一個驗證失敗后停止,不再驗證之后的規則 RuleFor(x => x.Surname).Cascade(CascadeMode.StopOnFirstFailure).NotNull().NotEqual("foo");
The two cascade modes are:
Continue(the default) - always invokes all validators in a rule definition
StopOnFirstFailure - stops executing a rule as soon as a validator fails
//在應用程序啟動入口設置全局級聯模式
//這會被具體的驗證器類和具體的驗證規則重寫
ValidatorOptions.CascadeMode = CascadeMode.StopOnFirstFailure;
//在單個驗證器類中設置級聯模式
public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        // First set the cascade mode
        CascadeMode = CascadeMode.StopOnFirstFailure;

        //Rule definitions follow
        //RuleFor(...)
        //RuleFor(...)
    }
}

e. Custom Validators 定制驗證器 

1.Using the Predicate Validator 使用斷言驗證器

public class Person
{
    public IList<Pet> Pets { get; set; } = new List<Pet>();
}
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");
    }
}
RuleFor(x
=> x.Pets).ListMustContainFewerThan(10);

Using a custom message placeholder 定制消息

//The resulting message will now be 'Pets' must contain fewer than 10 items.
public static IRuleBuilderOptions<T, IList<TElement>> ListMustContainFewerThan<T, TElement>(this IRuleBuilder<T, IList<TElement>> ruleBuilder, int num)
{
    return ruleBuilder.Must((rootObject, list, context) =>
    {
        context.MessageFormatter.AppendArgument("MaxElements", num);
        return list.Count < num;
    })
    .WithMessage("{PropertyName} must contain fewer than {MaxElements} items.");
}

public static IRuleBuilderOptions<T, IList<TElement>> ListMustContainFewerThan<T, TElement>(this IRuleBuilder<T, IList<TElement>> ruleBuilder, int num)
{
    return ruleBuilder.Must((rootObject, list, context) =>
    {
        context.MessageFormatter
          .AppendArgument("MaxElements", num)
          .AppendArgument("TotalElements", list.Count);

        return list.Count < num;
    })
    .WithMessage("{PropertyName} must contain fewer than {MaxElements} items. The list contains {TotalElements} element");
}

2.Using a Custom Validator 使用定制驗證器

//This method allows you to manually create the ValidationFailure instance associated with the validation error. 
RuleFor(x => x.Pets).Custom((list, context) =>
{
    if (list.Count > 10)
    {
        context.AddFailure("The list must contain 10 items or fewer");
        // It allows you to return multiple errors for the same rule 
        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");
    }
});

3.Writing a Custom, reusable Property Validator 使用定制可重用屬性驗證器

//若定制的邏輯非常復雜,則可將定制邏輯放入單獨的驗證類中
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;
    }
}
//調用方法一
RuleFor(person => person.Pets).SetValidator(new ListCountValidator<Pet>(10));


//調用方法二
public static IRuleBuilderOptions<T, IList<TElement>> ListMustContainFewerThan3<T, TElement>(this IRuleBuilder<T, IList<TElement>> ruleBuilder, int num)
{
    return ruleBuilder.SetValidator(new ListCountValidator<Pet>(10));
}
RuleFor(x
=> x.Pets).ListMustContainFewerThan(10);

 

4.Using AbstractValidator.Custom (Deprecated in 7.0) 抽象驗證器(棄用) 

//從7.0版本開始棄用,推薦上文中使用的 RuleFor(x => x).Custom((x, context) => ... )
public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        Custom(person => {
            return person.Pets.Count >= 10
               ? new ValidationFailure("Pets", "More than 9 pets is not allowed.")
               : null;
        });
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM