【Asp.Net MVC5】Fluent Validation 簡單範例

目前開始學習ASP.NET MVC5一些開發技巧,之後會開始學習.Net Core。

這次所要示範的是Fluent Validation,算是滿完整的一套驗證,擴充性也強,接著,為什麼我會認識到這套,主要是用Code First的Fluent API並不會對欄位做驗證(註:Fluent API只有單純Mapping),而我又不想用Data Annotation方式,會導致類非常的亂,還是喜歡類乾乾淨淨,驗證邏輯就拆開吧,當然比較麻煩些囉。此次文章只會涉及Fluent Validation。

Nuget套件安裝

建立User類別

我在[Model]資料夾下建立一個User類別。

namespace FluentValidationExample.Models
{
    public class User
    {
        [Display(Name = "姓名")]
        public string Name { get; set; }

        [Display(Name = "信箱")]
        public string Email { get; set; }

        [Display(Name = "生日")]
        public DateTime BirthdayTime { get; set; }

        [Display(Name = "密碼")]
        public string Password { get; set; }

        [Display(Name = "確認密碼")]
        public string ConfirmPassword { get; set; }

        [Display(Name = "金額")]
        public int Money { get; set; }

        [Display(Name = "年齡")]
        public int Age { get; set; }
    }
}

開始建立驗證規則

在專案中建立[Validation]資料夾,在底下建立一個ValidationUser類並實作抽象類別AbstractValidator。這裡開始撰寫驗證邏輯,示範如下。

namespace FluentValidationExample.Validation
{
    public class ValidationUser : AbstractValidator<User>
    {
        public ValidationUser()
        {
            // 當規則遇到第一個錯誤就停止不繼續
            // 如果想要該欄位全部錯誤資訊請選擇CascadeMode.Continue
            CascadeMode = CascadeMode.StopOnFirstFailure;

            RuleFor(u => u.Name)
                .NotEmpty().WithMessage("{PropertyName} 必須輸入值");

            RuleFor(u => u.Email)
                .NotEmpty().WithMessage("{PropertyName} 必須輸入值")
                .EmailAddress().WithMessage("{PropertyName} 請填寫正確");

            // 如果邏輯太複雜可用Must拆出來,例如檢查身分證居留證等
            RuleFor(u => u.BirthdayTime)
                .Must(ValidateAge).WithMessage("{PropertyName} 必須滿足18");

            RuleFor(u => u.Password)
                .NotEmpty().WithMessage("{PropertyName} 必須輸入值")
                .Must(p => p?.Length >= 8).WithMessage("{PropertyName} 必須大於等於8");

            RuleFor(u => u.ConfirmPassword)
                .NotEmpty().WithMessage("{PropertyName} 必須輸入值")
                .Equal(x => x.Password).WithMessage("密碼輸入不一");

            RuleFor(u => u.Money)
                .GreaterThan(100).WithMessage("{PropertyName} 必須大於 100");

            // 當When條件成立才去檢查數值
            RuleFor(u => u.Age)
                .GreaterThan(100).WithMessage("當金額999 {PropertyName} 必須大於 100歲").When(u => u.Money == 999);
        }

        private bool ValidateAge(DateTime Age_)
        {
            DateTime Current = DateTime.Today;
            int age = Current.Year - Convert.ToDateTime(Age_).Year;

            return age >= 18;
        }
    }
}

建立ValidatorFactory

在[Validation]建立ValidatorFactory類並實作ValidatorFactoryBase,建立此工廠是為了維護姓,往後只要ValidatorFactory()方法內增加對應的驗證即可,對於Controllers會非常乾淨。

namespace FluentValidationExample.Validation
{
    public class ValidatorFactory : ValidatorFactoryBase
    {
        private static Dictionary<Type, IValidator> validators = new Dictionary<Type, IValidator>();

        static ValidatorFactory()
        {
            // 往後增加驗證在此註冊
            validators.Add(typeof(IValidator<User>), new ValidationUser());
        }

        public override IValidator CreateInstance(Type validatorType)
        {
            IValidator validator;
            if (validators.TryGetValue(validatorType, out validator))
            {
                return validator;
            }
            return validator;
        }
    }
}

Global.asax註冊

建立好工廠後,接著,要去Global.asax做註冊動作。

namespace FluentValidationExample
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            ValidationConfiguration();
        }

        private void ValidationConfiguration()
        {
            FluentValidationModelValidatorProvider.Configure(provider =>
            {
                provider.ValidatorFactory = new ValidatorFactory();
            });
        }
    }
}

以上前置動作已完成,現在開始驗證。

建立UserController

namespace FluentValidationExample.Controllers
{
    public class UserController : Controller
    {
        // GET: User
        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Index(User data)
        {
            if (ModelState.IsValid)
            {
                // To do your application logic
            }

            return View(data);
        }
    }
}

Scaffold 建立頁面

用Scaffold快速建立驗證頁面。

驗證結果

成功不會出現錯誤。

參考資料

SourceCode

FluentValidation官方文檔解讀

ValidatorFactory參考