C# ASP-MVC中的动态验证
对于工作中的项目,我尝试创建一个流程,允许用户动态创建一个表单,然后其他用户可以填写表单的值。不过,我很难弄清楚如何使用ASP MVC 3实现内置模型绑定和验证 我们的视图模型是这样设置的。请注意,我过度简化了示例代码:C# ASP-MVC中的动态验证,c#,asp.net-mvc,asp.net-mvc-3,C#,Asp.net Mvc,Asp.net Mvc 3,对于工作中的项目,我尝试创建一个流程,允许用户动态创建一个表单,然后其他用户可以填写表单的值。不过,我很难弄清楚如何使用ASP MVC 3实现内置模型绑定和验证 我们的视图模型是这样设置的。请注意,我过度简化了示例代码: public class Form { public FieldValue[] FieldValues { get; set; } } public class Field { public bool IsRequired { get; set; } }
public class Form
{
public FieldValue[] FieldValues { get; set; }
}
public class Field
{
public bool IsRequired { get; set; }
}
public class FieldValue
{
public Field Field { get; set; }
public string Value { get; set; }
}
我们的观点是:
@model Form
@using (Html.BeginForm("Create", "Form", FormMethod.Post))
{
@for(var i = 0; i < Model.Fields.Count(); i++)
{
@Html.TextBoxFor(_ => @Model.Fields[i].Value)
}
<input type="submit" value="Save" name="Submit" />
}
@模型表单
@使用(Html.BeginForm(“Create”,“Form”,FormMethod.Post))
{
@对于(var i=0;i@Model.Fields[i].Value)
}
}
我希望我们能够创建一个定制的ModelValidatorProvider或ModelMetadataProvider类,该类能够分析FieldValue实例,确定其Field.IsRequired属性是否为true,然后将RequiredFieldValidator添加到该特定实例的验证器中。不过,我在这方面运气不好。似乎使用ModelValidatorProvider(和ModelMetadataProvider)无法访问父容器的值(即:将为FieldValue.value调用GetValidators(),但无法从中获取FieldValue对象)
我尝试过的事情:
- 在ModelValidatorProvider中,我尝试使用 ControllerContext.Controller.ViewData.Model,但如果 您有嵌套类型。如果我想找出验证器 Form.FieldValues[3],我不知道该使用哪个FieldValue
- 我尝试使用自定义ModelMetadata来尝试使用内部 modelAccessor的目标属性来获取父对象,但 如果有嵌套类型,则不起作用。MVC内部的某个地方 类似于我示例中的表达式将导致目标 模型的类型(形式),而不是字段值。所以我遇到了和你一样的问题 上面我不知道要比较哪个FieldValue实例 反对
- 我可以在FieldValue上添加的类级验证属性 类本身,但仅在服务器验证期间调用。我 也需要客户端验证
在MVC中我想做的事情可能吗?或者我完全缺少什么?一种可能是使用自定义验证属性 但是在开始实现之前,我想指出您的场景中的一个潜在缺陷。
IsRequired
属性是模型的一部分。这意味着在提交表单时,必须知道表单的值,以便有条件地将所需规则应用于相应的属性。但要在提交表单时知道此值,这意味着它必须是表单的一部分(作为隐藏或标准输入字段),或者必须从某处(数据存储,…)检索。第一种方法的问题很明显=>隐藏字段意味着用户可以设置他喜欢的任何值,因此不再是真正的验证,因为是用户决定需要哪个字段
鉴于此警告,我们假设您信任您的用户,并决定采用隐藏字段方法来存储IsRequired值。让我们看看示例实现如何:
型号:
public class Form
{
public FieldValue[] Fields { get; set; }
}
public class FieldValue
{
public Field Field { get; set; }
[ConditionalRequired("Field")]
public string Value { get; set; }
}
public class Field
{
public bool IsRequired { get; set; }
}
控制器:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new Form
{
Fields = new[]
{
new FieldValue { Field = new Field { IsRequired = true }, Value = "" },
new FieldValue { Field = new Field { IsRequired = true }, Value = "" },
new FieldValue { Field = new Field { IsRequired = false }, Value = "value 3" },
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(Form model)
{
return View(model);
}
}
视图:
在MVC中这是绝对可能的,但它相当复杂。MVC团队的Brad Wilson有一段视频,他描述了如何围绕字典构建动态验证系统。如何将结果绑定到数组?您使用定制的模型活页夹吗?谢谢回答Darin。不过,我不确定这是否能解决问题。我想使用已经存在的验证器属性,而不必重新实现它们。不过,感谢您指出安全问题。我们已经讨论过了(验证一个字段是否属于表单),但它不是我的示例代码的一部分。哦,我明白了。那怎么样?它是我用来在ASP.NET MVC应用程序中执行验证的框架。使用它实现您的场景非常简单。
@model Form
@using (Html.BeginForm())
{
@Html.EditorFor(x => x.Fields)
<input type="submit" value="Save" name="Submit" />
}
public class ConditionalRequiredAttribute : ValidationAttribute, IClientValidatable
{
private RequiredAttribute _innerAttribute = new RequiredAttribute();
private readonly string _fieldProperty;
public ConditionalRequiredAttribute(string fieldProperty)
{
_fieldProperty = fieldProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var containerType = validationContext.ObjectInstance.GetType();
var field = containerType.GetProperty(_fieldProperty);
if (field == null)
{
return new ValidationResult(string.Format("Unknown property {0}", _fieldProperty));
}
var fieldValue = (Field)field.GetValue(validationContext.ObjectInstance, null);
if (fieldValue == null)
{
return new ValidationResult(string.Format("The property {0} was null", _fieldProperty));
}
if (fieldValue.IsRequired && !_innerAttribute.IsValid(value))
{
return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName });
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "conditionalrequired",
};
rule.ValidationParameters.Add("iserquiredproperty", _fieldProperty + ".IsRequired");
yield return rule;
}
}
(function ($) {
$.validator.unobtrusive.adapters.add('conditionalrequired', ['iserquiredproperty'], function (options) {
options.rules['conditionalrequired'] = options.params;
if (options.message) {
options.messages['conditionalrequired'] = options.message;
}
});
$.validator.addMethod('conditionalrequired', function (value, element, parameters) {
var name = $(element).attr('name'),
prefix = name.substr(0, name.lastIndexOf('.') + 1),
isRequiredFiledName = prefix + parameters.iserquiredproperty,
requiredElement = $(':hidden[name="' + isRequiredFiledName + '"]'),
isRequired = requiredElement.val().toLowerCase() === 'true';
if (!isRequired) {
return true;
}
return value && value !== '';
});
})(jQuery);