NopCommerce源码架构详解-使用FluentValidation创建Model自定义Validator验证源码分析

一个健全的系统少不了大量的输入合法性验证,但是nop中的验证使用的是一个优秀的开源的验证框架。它的好处是,它可以采用lambda表达式的形式配置要验证的字段和错误信息。

NopCommerce源码架构详解概述

文章目录

概述

NopCommerce源码架构详解-使用FluentValidation创建Model自定义Validator验证源码分析。

内容

一个健全的系统少不了大量的输入合法性验证,但是nop中的验证使用的是一个优秀的开源的验证框架。它的好处是,它可以采用lambda表达式的形式配置要验证的字段和错误信息。今天我们就来看看Nop中是怎么使用FluentValidation创建Model自定义Validator验证的。

FluentValidation的官方地址:https://github.com/JeremySkinner/FluentValidation

当然你也可以在VS中用nuget快速获取安装到你的项目中。

我们可以在Nop的解决方案中的三个项目中都能找到一个叫做Validators的文件夹。

2020-04-23-18-01-16

里面就放的使用FluentValidation的API配置的验证器。这三个项目分别为Nop.Admin,Nop.Web,Nop.Web.Framework。

下面我们就找一个项目中Nop.Admin的一个Validator看看具体FluentValidation是怎么使用的。

using FluentValidation;
using Nop.Admin.Models.Blogs;
using Nop.Services.Localization;

namespace Nop.Admin.Validators.Blogs
{
    public class BlogPostValidator : AbstractValidator<BlogPostModel>
    {
        public BlogPostValidator(ILocalizationService localizationService)
        {
            RuleFor(x => x.Title)
                .NotEmpty()
                .WithMessage(localizationService.GetResource("Admin.ContentManagement.Blog.BlogPosts.Fields.Title.Required"));

            RuleFor(x => x.Body)
                .NotEmpty()
                .WithMessage(localizationService.GetResource("Admin.ContentManagement.Blog.BlogPosts.Fields.Body.Required"));
        }
    }
}

上面这个BlogPostValidator是管理后台项目的博客文章验证,我们可以看到里面是通过RuleFor来定义验证规则的,里面一共定义有两条验证规则,分别是标题Title和内容Body不能为空。当验证不通过时就会输出用.WithMessage包含的错误提示信息。

要使上面配置的验证Validator生效,我们还需要更改Global.asax:

protected void Application_Start()
{
    //省略其它代码...
    //fluent validation
    DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
    ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(new NopValidatorFactory()));
    //省略其它代码...
}

上面FluentValidationModelValidatorProvider构造函数参数传的是一个重写了GetValidator方法的NopValidatorFactory。

using System;
using FluentValidation;
using FluentValidation.Attributes;
using Nop.Core.Infrastructure;

namespace Nop.Web.Framework
{
    public class NopValidatorFactory : AttributedValidatorFactory
    {
        //private readonly InstanceCache _cache = new InstanceCache();
        public override IValidator GetValidator(Type type)
        {
            if (type != null)
            {
                var attribute = (ValidatorAttribute)Attribute.GetCustomAttribute(type, typeof(ValidatorAttribute));
                if ((attribute != null) && (attribute.ValidatorType != null))
                {
                    //validators can depend on some customer specific settings (such as working language)
                    //that's why we do not cache validators
                    //var instance = _cache.GetOrCreateInstance(attribute.ValidatorType,
                    //                           x => EngineContext.Current.ContainerManager.ResolveUnregistered(x));
                    var instance = EngineContext.Current.ContainerManager.ResolveUnregistered(attribute.ValidatorType);
                    return instance as IValidator;
                }
            }
            return null;

        }
    }
}

Nop除了写了很多Model的Validator,它还有一些属性验证。如验证信用卡号的CreditCardPropertyValidator:

using System;
using System.linq;
using FluentValidation.Validators;

namespace Nop.Web.Framework.Validators
{
    public class CreditCardPropertyValidator : PropertyValidator
    {
        public CreditCardPropertyValidator()
            : base("Credit card number is not valid")
        {

        }

        protected override bool IsValid(PropertyValidatorContext context)
        {
            var ccValue = context.PropertyValue as string;
            if (String.IsNullOrWhiteSpace(ccValue))
                return false;

            ccValue = ccValue.Replace(" ", "");
            ccValue = ccValue.Replace("-", "");

            int checksum = 0;
            bool evenDigit = false;

            //http://www.beachnet.com/~hstiles/cardtype.html
            foreach (char digit in ccValue.Reverse())
            {
                if (!Char.IsDigit(digit))
                    return false;

                int digitValue = (digit - '0') * (evenDigit ? 2 : 1);
                evenDigit = !evenDigit;

                while (digitValue > 0)
                {
                    checksum += digitValue % 10;
                    digitValue /= 10;
                }
            }

            return (checksum % 10) == 0;
        }
    }
}

属性验证器继承于PropertyValidator,把验证逻辑写在方法IsValid中。接下来Nop写了一个IRuleBuilder的扩展方法IsCreditCard来使用这个验证信用卡号的Validator:

using FluentValidation;

namespace Nop.Web.Framework.Validators
{
    public static class MyValidatorExtensions
    {
        public static IRuleBuilderOptions<T, string> IsCreditCard<T>(this IRuleBuilder<T, string> ruleBuilder)
        {
            return ruleBuilder.SetValidator(new CreditCardPropertyValidator());
        }
    }
}
原文出处:蓝狐软件工作室【蓝狐】

原文链接:http://m.lanhusoft.com/Article/349.html

本文观点不代表 .Net中文网 立场,转载请联系原作者。

发表评论

登录后才能评论