asp.net/MVC自定义模型验证属性不起作用



(我已经取得了一些进展,但仍然无法工作,更新如下…(

我正在尝试实现旧的开始日期不大于结束日期验证。这是我第一次尝试编写自定义验证属性。根据我在这里读到的内容,这就是我的想法。。。

自定义验证属性:

public class DateGreaterThanAttribute : ValidationAttribute
{
private string _startDatePropertyName;
public DateGreaterThanAttribute(string startDatePropertyName)
{
_startDatePropertyName = startDatePropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var propertyInfo = validationContext.ObjectType.GetProperty(_startDatePropertyName);
if (propertyInfo == null)
{
return new ValidationResult(string.Format("Unknown property {0}", _startDatePropertyName));
}
var propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null);
if ((DateTime)value > (DateTime)propertyValue)
{
return ValidationResult.Success;
}
else
{
var startDateDisplayName = propertyInfo
.GetCustomAttributes(typeof(DisplayNameAttribute), true)
.Cast<DisplayNameAttribute>()
.Single()
.DisplayName;
return new ValidationResult(validationContext.DisplayName + " must be later than " + startDateDisplayName + ".");
}
}
}

视图模型:

public class AddTranscriptViewModel : IValidatableObject
{
...
[DisplayName("Class Start"), Required]
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
[RegularExpression(@"^(1[012]|0?[1-9])[/]([12][0-9]|3[01]|0?[1-9])[/](19|20)dd.*", ErrorMessage = "Date out of range.")]
public DateTime? ClassStart { get; set; }
[DisplayName("Class End"), Required]
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
[RegularExpression(@"^(1[012]|0?[1-9])[/]([12][0-9]|3[01]|0?[1-9])[/](19|20)dd.*", ErrorMessage = "Date out of range.")]
[DateGreaterThan("ClassStart")]
public DateTime? ClassEnd { get; set; }
...
}

前端相关部分:

@using (Html.BeginForm("AddManualTranscript", "StudentManagement", FormMethod.Post, new { id = "studentManagementForm", @class = "container form-horizontal" }))
{
...
<div class="col-md-4" id="divUpdateStudent">@Html.Button("Save Transcript Information", "verify()", false, "button")</div>
...
<div class="col-md-2">
<div id="divClassStart">
<div>@Html.LabelFor(d => d.ClassStart, new { @class = "control-label" })</div>
<div>@Html.EditorFor(d => d.ClassStart, new { @class = "form-control" }) </div>
<div>@Html.ValidationMessageFor(d => d.ClassStart)</div>
</div>
</div>
<div class="col-md-2">
<div id="divClassEnd">
<div>@Html.LabelFor(d => d.ClassEnd, new { @class = "control-label" })</div>
<div>@Html.EditorFor(d => d.ClassEnd, new { @class = "form-control" }) </div>
<div>@Html.ValidationMessageFor(d => d.ClassEnd)</div>
</div>
</div>
...
}
<script type="text/javascript">
...
function verify() {
if ($("#StudentGrades").data("tGrid").total == 0) {
alert("Please enter at least one Functional Area for the transcript grades.");
}
else {
$('#studentManagementForm').trigger(jQuery.Event("submit"));
}
}
...
</script>

我看到的行为是,表单上所有其他字段的所有其他验证,都是标准验证,如Required、StringLength和RegularExpression等,都按预期工作:当我单击"保存"按钮时,那些未通过的字段会显示红色文本。我在IsValid代码中设置了一个断点,除非所有其他验证都通过,否则它不会成功。即便如此,如果验证检查失败,也不会停止发布。

进一步阅读后,我在Global.asax.cs中添加了以下内容:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(DateGreaterThanAttribute), typeof(DataAnnotationsModelValidator));

但这并没有什么区别。我还在回发函数中测试了ModelState.IsValid,结果是false。但对于其他验证器来说,如果永远不会走那么远的话。我甚至在标记中注意到,在生成页面时,似乎在那些具有验证属性的字段上创建了很多标记。这种魔力发生在哪里?为什么我的自定义验证器不在循环中?

有很多变化,但我在这里看到的似乎与我所看到的大致一致。我也读过一些关于在客户端注册验证器的文章,但这似乎只适用于客户端验证,而不适用于提交/发布时的模型验证。如果答案是我愚蠢的疏忽,我不会感到尴尬。大约一天后,我只需要它就可以工作了。

更新:

Rob的回答让我找到了下面评论中引用的链接,然后我来到了自定义验证属性中的客户端验证-asp.net mvc 4,这让我来到了这里https://thewayofcode.wordpress.com/tag/custom-unobtrusive-validation/

我在那里读到的与我所观察到的不一致,即标记中缺少了一些东西,看起来作者概述了如何将其放入其中。因此,我在我的验证属性类中添加了以下内容:

public class DateGreaterThanAttribute : ValidationAttribute, IClientValidatable // IClientValidatable added here
...
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
//string errorMessage = this.FormatErrorMessage(metadata.DisplayName);
string errorMessage = ErrorMessageString;
// The value we set here are needed by the jQuery adapter
ModelClientValidationRule dateGreaterThanRule = new ModelClientValidationRule
{
ErrorMessage = errorMessage,
ValidationType = "dategreaterthan" // This is the name the jQuery adapter will use, "startdatepropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
};
dateGreaterThanRule.ValidationParameters.Add("startdatepropertyname", _startDatePropertyName);
yield return dateGreaterThanRule;
}

并创建了这个JavaScript文件:

(function ($) {
$.validator.addMethod("dategreaterthan", function (value, element, params) {
console.log("method");
return Date.parse(value) > Date.parse($(params).val());
});
$.validator.unobtrusive.adapters.add("dategreaterthan", ["startdatepropertyname"], function (options) {
console.log("adaptor");
options.rules["dategreaterthan"] = "#" + options.params.startdatepropertyname;
options.messages["dategreaterthan"] = options.message;
});
})(jQuery);

(注意console.log点击……我从来没有看到过。(

在这之后,当我浏览到DataGreaterThanAttribute构造函数和GetClientValidationRules中的页面时,我现在会受到点击。同样,ClassEnd输入标记中现在有以下标记:

data-val-dategreaterthan="The field {0} is invalid." data-val-dategreaterthan-startdatepropertyname="ClassStart"

所以我越来越近了。问题是,addMethod和adapater.add似乎没有做好他们的工作。当我在控制台中使用以下工具检查这些对象时:

$.validator.methods
$.validator.unobtrusive.adapters

我添加的方法和适配器不在那里。如果我在控制台中运行JavaScript文件中的代码,它们确实会被添加并存在。我还注意到,如果我通常使用。。。

$("#studentManagementForm").data('unobtrusiveValidation')

没有证据表明我的自定义验证。

正如我之前提到的,这里有很多例子,它们的做法似乎都有点不同,所以我仍在尝试一些不同的东西。但我真的希望有一个以前打败过这件事的人能来和我分享这把锤子

如果我不能做到这一点,我会戴上安全帽,写一些破解的JavaScript来欺骗同样的功能。

我认为您需要IEnumerable<验证结果>在您的模型上。

大约4年前,我不得不做一些类似的事情,如果这有帮助的话,我手头还有这个片段:

public class ResultsModel : IValidatableObject
{
[Required(ErrorMessage = "Please select the from date")]
public DateTime? FromDate { get; set; }
[Required(ErrorMessage = "Please select the to date")]
public DateTime? ToDate { get; set; }
IEnumerable<ValidationResult> IValidatableObject.Validate(ValidationContext validationContext)
{
var result = new List<ValidationResult>();
if (ToDate < FromDate)
{
var vr = new ValidationResult("The to date cannot be before the from date");
result.Add(vr);
}
return result;
}
}