如何在模型上为复杂类型添加自定义ClientValidationRules(不引人注意的验证)?

时间:2014-01-07 19:30:22

标签: asp.net asp.net-mvc razor asp.net-mvc-5

假设我有一个自定义验证属性ValidateFooIsCompatibleWith模型,如下所示:

public class FooPart
{
    public string Foo { get; set; }

    public string Eey { get; set; }
}

public class FooableViewModel
{
    public FooPart Foo1 { get; set; }

    [ValidateFooIsCompatibleWith("Foo1")]
    public FooPart Foo2 { get; set; }
}

假设我也为FooPart定义了自定义的EditorTemplates:

 @Html.TextBoxFor(m => m.Foo)
 @Html.TextBoxFor(m => m.Eey)

因此我的观点基本上是:

@Html.EditorFor(m => m.Foo1)
@Html.EditorFor(m => m.Foo2)

服务器端,验证工作正常。但是,无论我尝试什么,我都无法获得渲染的html来添加规则。

如果我实施IClientValidatable,事实证明GetClientValidationRules()永远不会被调用。 (我之前已成功使用IClientValidatable和“简单”字段。

我还尝试通过继承DataAnnotationsModelValidator<TAttribute>注册我自己的自定义适配器,然后在global.asax中使用DataAnnotationsModelValidatorProvider.RegisterAdapter(...)进行注册。该方法也无法调用GetClientValidationRules()

**更新** 如果添加自定义ModelMetadataProvider和自定义ModelValidatorProvider以便我可以设置断点,我会注意到一些有趣的行为:

  • 已向ModelMetadataProvider请求ContainerType FooableViewModelModelType FooPart的元数据。但是,没有对ModelValidatorProvider提出相应的请求,因此我无法在那里插入自定义客户端验证规则。
  • ModelValidatorProvider和{{ContainerType FooPart ModelType string Foo EeyFooPart 1}}属性。但在此级别,我不知道应用于{{1}}属性的属性。

如何让MVC框架为复杂类型注册我的自定义客户端验证规则?

2 个答案:

答案 0 :(得分:1)

我找到了解决方案:

首先,创建一个自定义模型元数据提供程序(请参阅https://stackoverflow.com/a/20983571/24954),该提供程序检查复杂类型的属性,并将客户端可验证的规则工厂存储在AdditionalValues集合中,例如在CreateMetadataProtoype

CachedDataAnnotationsModelMetadataProvider覆盖中
 var ruleFactories = new List<Func<ModelMetadata, ControllerContext, IEnumerable<ModelClientValidationRules>>>();
 ...
 var clientValidatable = (IClientValidatable)attribute;
 ruleFactories.Add(clientValidatable.GetClientValidationRules);
 ...
 result.AdditionalValues.Add("mycachekey", ruleFactories);

接下来,将其注册为global.asax

中的默认元数据提供程序
protected void Application_Start()
{
    ModelMetadataProviders.Current = new MyCustomModelMetadataProvider();
    ....
}

然后我创建了一个html助手,它将处理modelmetata并创建/合并每个AdditionalValues集合中的“data-val *”html属性。

public static IDictionary<string, Object> MergeHtmlAttributes<TModel>(this HtmlHelper<TModel>, object htmlAttributes = null)
{
    var attributesDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
    //ensure data dictionary has unobtrusive validation enabled for the element
    attributesDictionary.Add("data-val", "true");

    //loop through all the rule factories, and execute each factory to get all the rules
    var rules = ruleFactory(helper.Html.ViewData.ModelMetadata, helper.Html.ViewContext);

    //loop through and execute all rules in the ruleFactory collection in the AdditionalValues
    //and add the data-val attributes for those.
    attributesDictionary.add("data-val-" + rule.ValidationType, ruleErrorMessage);

    //similarly for anything in the rule.ValidationParameters
    attributesDictionary.Add("data-val-" + rule.ValidationType + "-" + parameterName, parameterValue);

}

最后,在我的编辑器模板中,为每个复杂类型属性调用html帮助器(其模型类型为“FooPart1”),例如

 @Html.TextBoxFor(m => m.Foo, Html.MergeHtmlAttributes(new { @class="Bar"}))
 @Html.TextBoxFor(m => m.Eey, Html.MergeHtmlAttributes())

我实际上最终创建了第二个接口(与IClientValidatable具有相同的签名),这允许我为复杂类型的各个字段自定义规则(主要用于错误消息)。我还扩展了帮助程序以获取可以使用我的自定义规则格式化的字符串参数。

答案 1 :(得分:0)

jQuery.validator.setDefaults({
  success: "valid"
});
$( "#foo" ).validate({
rules: 
{
   rule1: {required: true, min: 3},
   parent: 
   {
        required: function(element) {return $("#age").val() < 13;}    
   }
 }
});

复杂类型似乎没有任何理由让我烦恼所以试试Jquery验证器。根据您尝试验证的内容,可能会完成工作。