广州知名网站建设网页设计服务,广开网络教学平台,企业文化手册,湛江网站FluentValidation 是 .NET 下的模型验证组件#xff0c;和 ASP.NET MVC 基于Attribute 声明式验证的不同处#xff0c;其利用表达式语法链式编程#xff0c;使得验证组件与实体分开。正如 FluentValidation 的 介绍#xff1a; A small validation library for .NET that u…
FluentValidation 是 .NET 下的模型验证组件和 ASP.NET MVC 基于Attribute 声明式验证的不同处其利用表达式语法链式编程使得验证组件与实体分开。正如 FluentValidation 的 介绍 A small validation library for .NET that uses a fluent interface and lambda expressions for building validation rules for your business objects. 使用后只能用一句话来形容真乃神器也
项目地址http://fluentvalidation.codeplex.com/
想体验 Lambda Expression 流畅的感觉吗下面 lets go!
首先你需要通过 NuGet 获取 FluentValidation、FluentValidation.MVC3 包我当前使用的版本如下 ?xml version1.0 encodingutf-8?
packagespackage idFluentValidation version3.3.1.0 /package idFluentValidation.MVC3 version3.3.1.0 /
/packages 快速入门
1. 建立模型类
为了演示我这里建了一个 Person 类并且假设有下面这些 Property属性。 /// summary
/// 个人
/// /summary
public class Person
{/// summary/// 姓/// /summarypublic string Surname { get; set; }/// summary/// 名/// /summarypublic string Forename { get; set; }/// summary/// 公司/// /summarypublic string Company { get; set; }/// summary/// 地址/// /summarypublic string Address { get; set; }/// summary/// 邮政编码/// /summarypublic string Postcode { get; set; }/// summary/// 个人空间的地址的别名比如bruce-liu-cnblogs、cnblogs_bruce_liu/// /summarypublic string UserZoneUrl { get; set; }
} 根据 FluentValidation 的使用方法我们直接可以在 Person 类上面直接标记对应的 Validator比如 [Validator(typeof(PersonValidator))]。但如果我们的模型层Model Layer不允许修改假设并且你像我一样喜欢干净的模型层不想要标记太多业务型的 Attribute 时我们就使用继承的方式来标记在派生类上标记。下面我们建一个 Customer 类继承自 Person 类并且再增加 2 个 Property属性最后标记 Validator Attribute。 [Validator(typeof(CustomerValidator))]
public class Customer : Person
{/// summary/// 是否有折扣/// /summarypublic bool HasDiscount { get; set; }/// summary/// 折扣/// /summarypublic float Discount { get; set; }} 2. 建立模型类相应的 FluentValidation 验证类 public class CustomerValidator : AbstractValidatorCustomer
{public CustomerValidator(){// 在这里写验证规则比如// Cascade(FluentValidation.CascadeMode.StopOnFirstFailure) 可以指定当前 CustomerValidator 的验证模式可重写全局验证模式RuleFor(customer customer.Surname).Cascade(FluentValidation.CascadeMode.StopOnFirstFailure).NotEmpty().Length(3, int.MaxValue).WithLocalizedName(() 姓).WithLocalizedMessage(() 亲{PropertyName}不能为空字符串并且长度大于{0});// 更多...// 更多...}
} 3. 在 Global.asax 里面的 Application_Start 中配置 FluentValidation
默认情况下FluentValidation 使用的验证错误消息是英文的且官方自带的语言包中没有中文于是我自己就手动翻译建立了一个资源文件 FluentValidationResource.resx并且在 Global.asax 中配置。 protected void Application_Start()
{ConfigureFluentValidation();
}protected void ConfigureFluentValidation()
{// 设置 FluentValidation 默认的资源文件提供程序 - 中文资源ValidatorOptions.ResourceProviderType typeof(FluentValidationResource);/* 比如验证用户名 not null、not empty、length(2,int.MaxValue) 时链式验证时如果第一个验证失败则停止验证 */ValidatorOptions.CascadeMode CascadeMode.StopOnFirstFailure; // ValidatorOptions.CascadeMode 默认值为CascadeMode.Continue// 配置 FluentValidation 模型验证为默认的 ASP.NET MVC 模型验证FluentValidationModelValidatorProvider.Configure();
} FluentValidationResource 代码中的 Key-Value 如下PS由于不知道怎么贴 Resource 文件中的代码我就用截图了 翻译得不好请多多包涵从这里下载
4. 客户端调用
本来用控制台程序就可以调用的由于笔者建立的项目是 ASP.NET MVC 项目本文的重点也是 FluentValidation 在 ASP.NET MVC 中使用于是就在 Action 里面验证了。在 HomeController 的 Index 方法里面的代码如下 public ActionResult Index()
{/* 下面的例子验证 FluentValidation 在 .net 中的使用非特定与 ASP.NET MVC */Customer customer new Customer(); // 我们这里直接 new 了一个 Customer 类看看模型验证能否通过CustomerValidator validator new CustomerValidator();ValidationResult results validator.Validate(customer); // 或者抛出异常 validator.ValidateAndThrow(customer);bool validationSucceeded results.IsValid;IListValidationFailure failures results.Errors;StringBuilder textAppender new StringBuilder();if (!results.IsValid){foreach (var failureItem in failures){textAppender.Append(br/br/);textAppender.AppendFormat(引起失败的属性值为{0}br/, failureItem.AttemptedValue);textAppender.AppendFormat(被关联的失败状态为{0}br/, failureItem.CustomState);textAppender.AppendFormat(错误消息为{0}br/, failureItem.ErrorMessage);textAppender.AppendFormat(Property属性为{0}br/, failureItem.PropertyName);textAppender.Append(br/br/);}}ViewBag.Message textAppender.ToString();return View();
} 最后运行就能看到效果
进阶篇
1. 属性类Property Class的验证
既然是顾客那么顾客就可能会有订单我们建立一个 Order 类把 Customer 类作为 Order 类的一个 Property属性。 /// summary
/// 订单
/// /summary
[Validator(typeof(OrderValidator))]
public class Order
{public Customer Customer { get; set; }/// summary/// 价格/// /summarypublic decimal Price { get; set; }
} 相应的我们还需要建立一个验证类 OrderValidator。为了共用 CustomerValidator 类我们需要在 OrderValidator 类的构造函数中为 Order 类的 Customer 属性指定 Validator。 /// summary
/// 订单验证类
/// /summary
public class OrderValidator : AbstractValidatorOrder
{public OrderValidator() {RuleFor(order order.Price).NotNull().GreaterThanOrEqualTo(0m).WithLocalizedName(() 价格);// 重用 CustomerValidatorRuleFor(order order.Customer).SetValidator(new CustomerValidator());}
} 在 ASP.NET MVC 中使用时在 Action 方法的参数上可以像使用 Bind Attribute 一样 public ActionResult AddCustomer([Bind(Include Company, Exclude Address)]Customer customer) 使用 CustomizeValidator Attribute来指定要验证的 Property属性 [HttpGet]
public ActionResult AddCustomer()
{return View(new Customer());
}[HttpPost]
public ActionResult AddCustomer([CustomizeValidator(PropertiesSurname,Forename)] Customer customer)
{/* 在 Action 的参数上标记 CustomizeValidator 可以指定 Interceptor拦截器、Properties要验证的属性以逗号分隔。如果指定了 Properties 要验证的属性以逗号分隔请注意是否别的属性有客户端验证导致客户端提交不了而服务器端又可以不用验证。*/if (!ModelState.IsValid){return View(customer);}return Content(验证通过);
} 由此可见FluentValidation 真是用心良苦这都想到了不容易啊 扩展篇
1. 完善 CustomerValidator
接下来我们继续 完善 CustomerValidator 增加更多的验证规则。 public class CustomerValidator : AbstractValidatorCustomer
{public CustomerValidator(){// CascadeMode CascadeMode.StopOnFirstFailure; 可以指定当前 CustomerValidator 的验证模式可重写全局验证模式RuleFor(customer customer.Surname).Cascade(FluentValidation.CascadeMode.StopOnFirstFailure).NotEmpty().Length(3, int.MaxValue).WithLocalizedName(() 姓).WithLocalizedMessage(() 亲{PropertyName}不能为空字符串并且长度大于{0});// 注意调用 Cascade(FluentValidation.CascadeMode.StopOnFirstFailure) 表示当一个验证条件失败后不再继续验证RuleFor(customer customer.Forename).NotEmpty().WithLocalizedName(() 名).WithLocalizedMessage(() {PropertyName} 一定要不为空Do you know ?);RuleFor(customer customer.Company).NotNull().WithLocalizedName(() 公司名称).WithMessage(string.Format({{PropertyName}} 不能 \{0}\下次记住哦{1}, 为空, 呵呵));RuleFor(customer customer.Discount).NotEqual(0).WithLocalizedName(() 折扣).When(customer customer.HasDiscount);RuleFor(customer customer.Address).Length(20, 250).WithLocalizedName(() 地址).Matches(^[a-zA-Z]$).WithLocalizedMessage(() 地址的长度必须在 20 到 250 个字符之间并且只能是英文字符);RuleFor(customer customer.Postcode).Must(BeAValidPostcode).WithLocalizedName(() 邮政编码).WithMessage(请指定一个合法的邮政编码);// 注意如果用了 Must 验证方法则没有客户端验证。Custom((customer, validationContext) {bool flag1 customer.HasDiscount;bool flag2 !validationContext.IsChildContext;return flag1 flag2 customer.Discount 0 ? null : new ValidationFailure(Discount, 折扣错误, customer.Discount);});}/// summary/// 检查是否是合法的邮政编码/// /summary/// param namepostcode/param/// returns/returnsprivate bool BeAValidPostcode(string postcode){if (!string.IsNullOrEmpty(postcode) postcode.Length 6){return true;}return false;}
} 当我想要给 Customer.UserZoneUrl个人空间的地址的别名 写验证规则的时候我发现它的验证规则可以提取出来方便下次有类似的功能需要用到。那能不能像调用 NotNull() 、NoEmpty() 方法那样调用我们写的 EntryName() 呢答案当然可以
这样调用怎么样 RuleFor(customer customer.UserZoneUrl).EntryName(); 其中 EntryName() 是一个扩展方法。 using FluentValidation;public static class FluentValidatorExtensions
{public static IRuleBuilderOptionsT, string EntryNameT(this IRuleBuilderT, string ruleBuilder){return ruleBuilder.SetValidator(new EntryNameValidator());}
} 我们看到调用 EntryName 扩展方法其实是调用另外一个 Validator - EntryNameValidator。 public class EntryNameValidator : PropertyValidator, IRegularExpressionValidator
{private readonly Regex regex;const string expression ^[a-zA-Z0-9][\w-_]{1,149}$;public EntryNameValidator(): base(() ExtensionResource.EntryName_Error){regex new Regex(expression, RegexOptions.IgnoreCase);}protected override bool IsValid(PropertyValidatorContext context){if (context.PropertyValue null) return true;if (!regex.IsMatch((string)context.PropertyValue)){return false;}return true;}public string Expression{get { return expression; }}
} 这里我们的 EntryNameValidator 除了继承自 PropertyValidator还实现了 IRegularExpressionValidator 接口。为什么要实现 IRegularExpressionValidator 接口 呢是因为可以共享由 FluentValidation 带来的好处比如客户端验证等等。
其中 ExtensionResource 是一个资源文件我用来扩展 FluentValidation 时使用的资源文件。 2. 复杂验证
下面我们再建立一个 Pet宠物类为 Customer 类增加一个 public ListPet Pets { get; set; } 属性。 /// summary
/// 顾客类
/// /summary
[Validator(typeof(CustomerValidator))]
public class Customer : Person
{/// summary/// 是否有折扣/// /summarypublic bool HasDiscount { get; set; }/// summary/// 折扣/// /summarypublic float Discount { get; set; }/// summary/// 一个或多个宠物/// /summarypublic ListPet Pets { get; set; }}/// summary
/// 宠物类
/// /summary
public class Pet
{public string Name { get; set; }
} 那 FluentValidation 对集合的验证该如何验证呢下面我们要求顾客的宠物不能超过 10 个。你一定想到了用下面的代码实现 Custom(customer
{return customer.Pets.Count 10? new ValidationFailure(Pets, 不能操作 10 个元素): null;
}); 或者我们写一个自定义的 Property属性验证器 ListMustContainFewerThanTenItemsValidatorT让它继承自 PropertyValidator public class ListMustContainFewerThanTenItemsValidatorT : PropertyValidator
{public ListMustContainFewerThanTenItemsValidator(): base(属性 {PropertyName} 不能超过 10 个元素!){// 注意这里的错误消息也可以用资源文件}protected override bool IsValid(PropertyValidatorContext context){var list context.PropertyValue as IListT;if (list ! null list.Count 10){return false;}return true;}
} 应用这个属性验证器就很容易了在 Customer 的构造函数中 RuleFor(customer customer.Pets).SetValidator(new ListMustContainFewerThanTenItemsValidatorPet()); 再或者为了公用写一个扩展方法扩展 IRuleBuilderT, IListTElement 类 /// summary
/// 定义扩展方法是为了方便调用。
/// /summary
public static class MyValidatorExtensions
{public static IRuleBuilderOptionsT, IListTElement MustContainFewerThanTenItemsT, TElement(this IRuleBuilderT, IListTElement ruleBuilder){return ruleBuilder.SetValidator(new ListMustContainFewerThanTenItemsValidatorTElement());}
} 调用也像上面调用 EntryName() 一样直接调用 RuleFor(customer customer.Pets).MustContainFewerThanTenItems(); 3. 与 IoC 容器Autofac、Unity、StructureMap等集成
下面以 Autofac 为例进行演示
1. 创建自己的 ValidatorFactory
比如我这里创建为 AutofacValidatorFactory继承自 FluentValidation.ValidatorFactoryBase而 ValidatorFactoryBase 本身是实现了 IValidatorFactory 的。IValidatorFactory 的代码如下 // 摘要:
// Gets validators for a particular type.
public interface IValidatorFactory
{// 摘要:// Gets the validator for the specified type.IValidatorT GetValidatorT();//// 摘要:// Gets the validator for the specified type.IValidator GetValidator(Type type);
} ValidatorFactoryBase 的代码如下 public abstract class ValidatorFactoryBase : IValidatorFactory
{protected ValidatorFactoryBase();public abstract IValidator CreateInstance(Type validatorType);public IValidatorT GetValidatorT();public IValidator GetValidator(Type type);
} 我们看到 ValidatorFactoryBase 其实是把 IValidatorFactory 接口的 2 个方法给实现了但核心部分还是抽象出来了那我们的 AutofacValidatorFactory 需要根据 Autofac 的使用方法进行编码代码如下 public class AutofacValidatorFactory : ValidatorFactoryBase
{private readonly IContainer _container;public AutofacValidatorFactory(IContainer container){_container container;}/// summary/// 尝试创建实例返回值为 NULL 表示不应用 FluentValidation 来做 MVC 的模型验证/// /summary/// param namevalidatorType/param/// returns/returnspublic override IValidator CreateInstance(Type validatorType){object instance;if (_container.TryResolve(validatorType, out instance)){return instance as IValidator;}return null;}
} 2. 在 Application_Start 中注册 Autofac protected void Application_Start()
{RegisterAutofac();
}protected void RegisterAutofac()
{// 注册 IoCContainerBuilder builder new ContainerBuilder();builder.RegisterNewsManagement();// 创建 containerIContainer _container builder.Build();// 在 NewsManagement 模型下设置 container_container.SetAsNewsManagementResolver();ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(new AutofacValidatorFactory(_container)));DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes false;
} 其中上面那 2 个方法RegisterNewsManagement、SetAsNewsManagementResolver是扩展方法代码如下 public static class AutofacExtensions
{public static void RegisterNewsManagement(this ContainerBuilder builder){builder.RegisterTypeNewsCategoryValidator().AsIValidatorNewsCategoryModel();builder.RegisterTypeNewsValidator().AsIValidatorNewsModel();builder.RegisterControllers(typeof(MvcApplication).Assembly);}public static void SetAsNewsManagementResolver(this IContainer contaner){DependencyResolver.SetResolver(new AutofacDependencyResolver(contaner));}
} 至此我们的模型上面就可以注释掉对应的 Attribute 了。 /// summary
/// 文章表模型
/// /summary//[Validator(typeof(NewsValidator))]
public class NewsModel : NewsEntity
{} --------------------- 作者惟楚有才 来源CSDN 原文https://blog.csdn.net/qq289523052/article/details/23739243 版权声明本文为作者原创文章转载请附上博文链接 内容解析ByCSDN,CNBLOG博客文章一键转载插件