这是我为超越简单数据注释模型的更复杂验证找到的最佳解决方案。
我敢肯定,我并不是唯一一个尝试实现的人IDataErrorInfo
,并且看到它只为我创建了两种实现方法。我在想等一下——我现在是否必须进去为所有事情从头开始编写我自己的自定义例程?而且 - 如果我有模型级别的东西要验证怎么办。除非您想在 IDataErrorInfo 实现中执行此类或此类操作,否则当您决定使用它时,您似乎只能靠自己了。
我碰巧遇到了和提问者完全相同的问题。我想验证美国邮政编码,但前提是国家/地区被选为美国。显然,模型级数据注释不会有任何好处,因为这不会导致邮政编码以红色突出显示为错误。[可以在 PropertiesMustMatchAttribute 类的 MVC 2 示例项目中找到类级别数据注释的好示例]。
解决方案非常简单:
首先你需要在 global.asax 中注册一个 modelbinder。如果需要,您可以将其作为类级别 [attribute] 执行,但我发现在 global.asax 中注册更加灵活。
private void RegisterModelBinders()
{
ModelBinders.Binders[typeof(UI.Address)] = new AddressModelBinder();
}
然后创建模型绑定器类,并编写您的复杂验证。您可以完全访问对象的所有属性。这将在任何数据注释运行后运行,因此如果您想反转任何验证属性的默认行为,您始终可以清除模型状态。
public class AddressModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext, bindingContext);
// get the address to validate
var address = (Address)bindingContext.Model;
// validate US zipcode
if (address.CountryCode == "US")
{
if (new Regex(@"^\d{5}([\-]\d{4})?$", RegexOptions.Compiled).
Match(address.ZipOrPostal ?? "").Success == false)
{
// not a valid zipcode so highlight the zipcode field
var ms = bindingContext.ModelState;
ms.AddModelError(bindingContext.ModelName + ".ZipOrPostal",
"The value " + address.ZipOrPostal + " is not a valid zipcode");
}
}
else {
// we don't care about the rest of the world right now
// so just rely on a [Required] attribute on ZipOrPostal
}
// all other modelbinding attributes such as [Required]
// will be processed as normal
}
}
这样做的美妙之处在于,您所有现有的验证属性仍然有效 - [Required]、[EmailValidator]、[MyCustomValidator] - 无论您拥有什么。
您可以根据需要将任何额外的代码添加到模型绑定器和设置字段中,或者模型级别的 ModelState 错误中。
请注意,对我来说 anAddress
是主模型的子模型 - 在这种情况下CheckoutModel
,它看起来像这样:
public class CheckoutModel
{
// uses AddressModelBinder
public Address BillingAddress { get; set; }
public Address ShippingAddress { get; set; }
// etc.
}
这就是为什么我必须这样做,以便为“BillingAddress.ZipOrPostal”和“ShippingAddress.ZipOrPostal”设置模型错误。bindingContext.ModelName
+ ".ZipOrPostal"
PS。来自“单元测试类型”的任何评论表示赞赏。我不确定这对单元测试的影响。