• Asp.net core 学习笔记 Fluent Validation


    2020-09-17

    在使用 when 的时候要留意一下, 不过是 true/false 里面的代码都会被执行, 它是先调用 action, setup 好 validation, 然后在 validation 的时候才去调用 when 看要不要处理. 

    所以不要把其它逻辑放进去哦

    WhenAsync(async (a, _) =>
    {
        await Task.Delay(1000);
        return false;
    }, () => 
    {
        // 一定会进来
        RuleFor(e => e.ID).NotEmpty();
    });

    2020-09-11

    有时候会用到异步 

    https://docs.fluentvalidation.net/en/latest/async.html

    比如 WhenAysnc, 记得哦, 如果有用到 WhenAsync 或者 MustAsync, 那么就需要调用 ValidateAsync 哦. 

    WhenAsync(async (e, cancellationToken) =>
    {
        var isDebtor = await creditTermService.IsDebtorOrCreditorAsync(e.creditTerm);
        return !isDebtor;
    }, () =>
    {
        RuleFor(e => e.creditLimit).Null();
    });

    2020-02-20

    NotEmpty vs NotNull

    public class SimpleTestData
    {
        public string? string1 { get; set; }
        public string string2 { get; set; } = "";
        public int? number1 { get; set; }
        public int numbe2 { get; set; }
    }

    json

    {
        "string1" : "",
        "string2" : "",
        "number1" : 0,
        "number2" : 0
    }

    result

    "errors": {
        "numbe2": [
          "'numbe2' must not be empty."
        ],
        "string1": [
          "'string1' must not be empty."
        ],
        "string2": [
          "'string2' must not be empty."
        ]
      }

    int 如果是 nullable 的话, empty 只是验证 != null, 0 是 ok 的, 因为 default(int?) 是 null. 符合逻辑. 

    int 如果不是 nullable, empty 验证 != 0 因为 default(int) 是 0

    但是 string 就不同了, 因为没有初始值, 所以 emptty 永远都会检查 "" empty string. 所以这个时候用 notnull 就比较合理了.

     not empty 对 List<string> length = 0 也是会保护哦

    2020-02-04

    InclusiveBetween vs ExclusiveBetween

    between 1 - 10, 没有说明 1 和 10 是不是 ok. (大部分情况下是)

    如果 1 和 10 ok 那么就是 InclusiveBetween 

    如果是 1 和 10 不可以, 2...9 才可以的话,那么是 ExclusiveBetween

    2019-10-24

    新功能,child validation 

    从前需要特地开一个 child validator 来写逻辑, 现在不需要咯

    public class CustomerValidator : AbstractValidator<Customer> {
      public CustomerValidator() {
        RuleForEach(x => x.Orders).ChildRules(order => {
          order.RuleFor(x => x.ProductName).NotNull();
          order.RuleFor(x => x.Amount).GreaterThan(0);
        });
      }
    }

    2019-08-18

    验证 decimal scale 和 precision 

    RuleFor(x => x.Amount).ScalePrecision(4, 9);

    留意哦,1st param 是 scale 2nd 才是 precision 

    和 sql 是相反的. sql 是 decimal(9,4), 真奇葩...

    issue : https://github.com/JeremySkinner/FluentValidation/issues/1008

    目前它的验证也和 sql 不同, 比如 decimal(9, 4), 意思是前面最多 5 位数. 但是 fluent validation 没有左右验证的概念. 所以这个是通过的,然后你输入 sql 就会报错了.

    作者说 9.0 可能修改这个. 让它和 sql 验证行为一致. 

    之前就有在 .net 时代介绍过了.

    这个 dll 也支持 .net core 而且一直有人维护. 

    对比 data annotation 的 validation, 我越来越觉得这个 fluent 好用多了. 

    一堆 Attribute 在 property 上面真的很乱. 

    安装很容易 

    nuget : https://www.nuget.org/packages/FluentValidation.AspNetCore/

    然后 startup.cs

      services.AddMvc().AddFluentValidation(fvc => fvc.RegisterValidatorsFromAssemblyContaining<Startup>()).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

     定义规则 : getting started refer : https://fluentvalidation.net/start

            public class ProductPostData
            {
                public string code { get; set; }
            }
    
            public class ProductPostDataValidator : AbstractValidator<ProductPostData>
            {
                public ProductPostDataValidator()
                {
                    RuleFor(product => product.code).NotEmpty();
                }
            }

    build in rule :

    https://fluentvalidation.net/built-in-validators 

    complex 的做法 : 

    写一个 child validator, 然后 SetValidator. 一定要 set 哦, 不会自动的

    RuleFor(o => o.Address).NotNull();
    RuleFor(o => o.Address).SetValidator(new AddressValidator());

    或者直接定义, 不过要记得另外处理 null 的情况哦, 还有一般的 If 

    RuleFor(o => o.Address).NotNull();
    RuleFor(o => o.Address.text1).NotEmpty().When(o => o.Address != null);

    也可以 if else 包起来 

    When(customer => customer.IsPreferred, () => {
       RuleFor(customer => customer.CustomerDiscount).GreaterThan(0);
       RuleFor(customer => customer.CreditCardNumber).NotNull();
    }).Otherwise(() => {
      RuleFor(customer => customer.CustomerDiscount).Equal(0);
    });

    这个很常用到 .Must()

    refer : https://fluentvalidation.net/custom-validators#predicate-validator

    custom valid

    https://fluentvalidation.net/custom-validators

    namespace Project.Validators
    {
        public static class CustomValidatorExtensions
        {
            public static IRuleBuilderOptions<T, string> IsKeatKeat<T>(
                this IRuleBuilder<T, string> ruleBuilder, string value)
            {
    
                return ruleBuilder.Must((rootObject, propertyValue, context) =>
                {
                    context.MessageFormatter.AppendArgument("value", value);
                    return (propertyValue as string) == "keatkeat";
                }).WithMessage("{PropertyValue} is not keatkeat {value}");
    
    
                //return ruleBuilder.SetValidator(new IsKeatKeatValidator(value));
            }
        }
         
        public class IsKeatKeatValidator : PropertyValidator
        {
            private readonly string Value;
            public IsKeatKeatValidator(string value)
                   : base("{PropertyValue} is not keatkeat {value}")
            {
                Value = value;
            }
            protected override bool IsValid(PropertyValidatorContext context)
            {
                if (context.PropertyValue == null) return false;
                var propertyValue = context.PropertyValue as string;
                context.MessageFormatter.AppendArgument("value", Value);
                return propertyValue == "keatkeat";
            }
        }
    }
    RuleFor(p => p.code).NotEmpty().IsKeatKeat("keatkeat");

    DI 和 async

    public ProductPostDataValidator(
        ApplicationDbContext Db
    )
    {
        //RuleFor(p => p.code).NotEmpty().IsKeatKeat("keatkeat");
        RuleFor(p => p.code).NotEmpty().MustAsync(async (id, cancellation) => 
        {
            var products =  await Db.Products.ToListAsync();
            return true;
        });
    }

    如果用 setValidator 那就要自己传进去了。

    多一个例子, 不用匿名方法

    public class SimpleTestDataValidator : AbstractValidator<SimpleTestData>
    {
        private readonly IHttpContextAccessor HttpContextAccessor;
        public SimpleTestDataValidator(
            IHttpContextAccessor httpContextAccessor
        )
        {
            HttpContextAccessor = httpContextAccessor;
            RuleFor(p => p.age).NotEmpty().MustAsync(BeSomething);
        }
    
        private async Task<bool> BeSomething(int age, CancellationToken cancellation)
        {
            await Task.Delay(2000);
            return true;
        }
    }

    array, not empty 就是 check null 和 count, 通过 foreach set 后面的

     RuleFor(p => p.colors).NotEmpty().ForEach(c => c.SetValidator(new ProductColorPostDataValidator()));

    错一个就停 CascadeMode, defualt 行为是会验证所有的错误.

    refer https://fluentvalidation.net/start#setting-the-cascade-mode#setting-the-cascade-mode

    RuleFor(p => p.age).NotEmpty();
    RuleFor(p => p.age).Cascade(CascadeMode.StopOnFirstFailure).Must(BeSomething).MustAsync(BeSomethingAsync);

     DependentRules 这个也是用于错就不要跑接下来的 

    RuleFor(p => p.name).NotEmpty();
    RuleFor(p => p.datas).NotEmpty().DependentRules(() => 
    {
        RuleForEach(p => p.datas).SetValidator(new DataValidator()).DependentRules(() =>
        {
            RuleFor(p => p.datas).Must(v => false);
        });
    });
    也可以针对整个 validator 
    public class PersonValidator : AbstractValidator<Person> {
      public PersonValidator() {
        // First set the cascade mode
        CascadeMode = CascadeMode.StopOnFirstFailure;
    
        RuleFor(x => x.Surname).NotNull().NotEqual("foo");
        RuleFor(x => x.Forename).NotNull().NotEqual("foo");
      }
    }

    甚至是全局 ( 注意这个是 static 属性来的,不需要通过 config options 来配置 )

    ValidatorOptions.CascadeMode = CascadeMode.StopOnFirstFailure;



  • 相关阅读:
    *args, **kwargs
    python format函数
    python自省
    生成器与迭代器
    python面试题
    xpath和gzip
    python正则表达式
    cookie
    random
    杭电1710 (已知二叉树前中序 求后序)
  • 原文地址:https://www.cnblogs.com/keatkeat/p/10746813.html
Copyright © 2020-2023  润新知