在ASP.NET MVC中使用IDataErrorInfo

4
我有一个简单的地址输入应用程序,我正在尝试使用IDataErrorInfo接口,如 asp.net网站上所解释
对于可以独立验证的项目,它非常有效,但是当某些项目依赖于其他项目时,效果不佳。例如,验证邮政编码取决于国家:
    private string _PostalCode;
    public string PostalCode
    {
        get
        {
            return _PostalCode;
        }
        set
        {
            switch (_Country)
            {
                case Countries.USA:
                    if (!Regex.IsMatch(value, @"^[0-9]{5}$"))
                        _errors.Add("PostalCode", "Invalid Zip Code");
                    break;
                case Countries.Canada:
                    if (!Regex.IsMatch(value, @"^([a-z][0-9][a-z]) ?([0-9][a-z][0-9])$", RegexOptions.IgnoreCase))
                        _errors.Add("PostalCode", "Invalid postal Code");
                    break;
                default:
                    throw new ArgumentException("Unknown Country");
            }
            _PostalCode = value;
        }
    }

所以只有在设置了国家之后才能验证邮政编码,但似乎没有办法控制这个顺序。
我可以使用IDataErrorInfo中的错误字符串,但这不会显示在Html.ValidationMessage旁边的字段。
3个回答

5

asp.net 上关于这个主题的教程 (http://www.asp.net/learn/mvc/tutorial-38-cs.aspx) 也许会有所帮助,它可以给你一个开始的思路。 - Jeff Siver

1

这是我发现的更复杂验证的最佳解决方案,超过了简单数据注释模型。

我相信我不是唯一一个尝试实现IDataErrorInfo并且发现它只为我创建了两种方法。 我在想等一下 - 我现在必须进去并从头开始编写自己的自定义程序吗? 而且 - 如果我有模型级别的事情要验证怎么办。除非你想从IDataErrorInfo实现内部做一些这样这样的事情,否则似乎你必须独自完成。

我碰巧遇到了与提问者完全相同的问题。我想要验证美国邮政编码,但仅当选择的国家为美国时才进行验证。显然,模型级别的数据注释是不好的,因为这不会导致邮政编码以错误的方式被突出显示。[在MVC 2示例项目中可以找到一个很好的类级别数据注释示例,即PropertiesMustMatchAttribute类]。

解决方案非常简单:

首先,您需要在global.asax中注册一个modelbinder。如果您愿意,您可以将其作为类级别[属性]执行,但我发现在global.asax中注册更加灵活。

private void RegisterModelBinders()
{
     ModelBinders.Binders[typeof(UI.Address)] = new AddressModelBinder();
}

接下来创建ModelBinder类,并编写复杂验证逻辑。您可以完全访问对象上的所有属性。它将在任何数据注释运行之后运行,因此,如果要更改任何验证属性的默认行为,则始终可以清除模型状态。

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错误。

请注意,对我来说,Address是主模型的子项 - 在这种情况下是CheckoutModel,它看起来像这样:

public class CheckoutModel
{
    // uses AddressModelBinder
    public Address BillingAddress { get; set; }
    public Address ShippingAddress { get; set; }

    // etc.
}

这就是为什么我必须执行bindingContext.ModelName+“。ZipOrPostal”,以便将模型错误设置为“BillingAddress.ZipOrPostal”和“ShippingAddress.ZipOrPostal”。

附言:欢迎“单元测试类型”的任何评论。我不确定这对单元测试的影响。


0
关于错误字符串、IDataErrorInfo和Html.ValidationMessage的评论,您可以使用以下方法显示对象级别与字段级别的错误消息:
Html.ValidationMessage("address", "Error")

Html.ValidationMessage("address.PostalCode", "Error")

在您的控制器中,使用[Bind(Prefix="address")]为对象的post方法处理程序参数进行装饰。在HTML中,将输入字段命名为...
<input id="address_PostalCode" name="address.PostalCode" ... />

我通常不使用Html助手。请注意id和name之间的命名约定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接