在视图中动态添加“data-val”和“data-val-required”是错误的吗?

时间:2013-11-15 14:47:01

标签: c# asp.net-mvc validation

我有一个可以用[Required]属性装饰的ViewModel(见下文)。我已经到了需要让客户端控制哪些字段是否需要的地步。他们可以配置这个槽XML,并且所有这些信息在首次创建时都存储在模型中。现在我的字段没有用[Required]修饰,但仍需要在提交之前进行验证(根据“用户设置”)(例如Phone字段)。

public class MyBusinessObjectViewModel
{
    [Required]
    public string Email { get; set; } //compulsory

    public string Phone { get; set; } //not (yet) compulsory, but might become
}

如果用户不输入Phone号码,数据仍会发布。为了不弄乱自定义验证器,我只需将“data-val”和“data-val-required”属性添加到Html中,如下所示:

Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("data-val", "true");
dict.Add("data-val-required", "This field is required.");
@Html.TextBoxFor(x => x, dict); 

这会强制客户端验证所有根据需要动态设置的属性。这是好习惯吗?我可以期待什么样的副作用?

2 个答案:

答案 0 :(得分:3)

您应该考虑使用您自己的metadata provider扩展元模型框架,以便在您的网站配置和模型元数据之间进行实际绑定。实际上,您可以在元数据创建过程中在属性模型元数据上将required property标志设置为true。我无法确定这是否会导致内置编辑器模板生成属性,但我认为确实如此。在最糟糕的情况下,您实际上可以创建新的RequiredAttribute并将其附加到该属性,这有点笨拙,但在某些情况下效果很好。

您也可以使用IMetadataAware属性执行此操作,尤其是如果Required是用户可以自定义的唯一元数据方面,但实现实际上取决于您尝试执行的操作。

在特定情况下使用自定义ModelMetadataProvider的一个主要优点是您可以使用依赖注入(通过ModelMetadataProviders)将您的客户设置持久性机制纳入范围,而使用data属性您只能编写创建元数据模型后立即运行的隔离方法。

以下是自定义模型元数据提供程序的示例实现,您只需将客户端设置更改为您要使用的任何设置。

已更新但未进行全面测试

public class ClientSettingsProvider
{
    public ClientSettingsProvider(/* db info */) { /* init */ }

    public bool IsPropertyRequired(string propertyIdentifier)
    {
       // check the property identifier here and return status
    }

}

public ClientRequiredAttribute : Attribute
{
    string _identifier;
    public string Identifier { get { return _identifer; } }
    public ClientRequiredAttribute(string identifier)
    { _identifier = identifier; }
}

public class RequiredModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    ClientSettings _clientSettings;

    public RequiredModelMetadataProvider(ClientSettings clientSettings)
    {
        _clientSettings = clientSettings;
    }

    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        // alternatively here is where you could 'inject' a RequiredAttribute into the attributes list

        var clientRequiredAttribute = attributes.OfType<ClientRequiredAttribute>().SingleOrDefault();
        if(clientRequiredAttribute != null && _clientSettings.IsPropertyRequired(clientRequiredAttribute.Identifier))
        {
            // By injecting the Required attribute here it will seem to 
            // the base provider we are extending as if the property was
            // marked with [Required]. Your data validation attributes should
            // be added, provide you are using the default editor templates in
            // you view.
            attributes = attributes.Union(new [] { new RequiredAttribute() });
        }

        var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

        // REMOVED, this is another way but I'm not 100% sure it will add your attributes
        // Use whatever attributes you need here as parameters...
        //if (_clientSettings.IsPropertyRequired(containerType, propertyName))
        //{
        //    metadata.IsRequired = true;
        //}

        return metadata;
    }
}

<强> USAGE

public class MyModel
{
     [ClientRequired("CompanyName")]
     public string Company { get; set; }
}

public class MyOtherModel
{
     [ClientRequired("CompanyName")]
     public string Name { get; set; }

     public string Address { get; set; }
}

这两个模型都会针对您的客户端设置提供程序验证字符串“CompanyName”。

答案 1 :(得分:2)

不想弄乱自定义验证器,因此您在视图中混淆了验证逻辑,方法是将其从预期找到它的位置移除。

真的,不要害怕创建自定义属性验证器。你现在正在做的是获得技术债务。