如何在ASP.NET MVCvalidation期间提供警告?

有时用户input不是严格无效的,但可以认为是有问题的。

例如:

  • 用户在单行Name字段中input一个长句子。 他可能应该使用Description字段
  • 用户input一个与现有实体非常相似的Name也许他在input相同的实体,但没有意识到它已经存在,或者一些并发用户刚刚进入它。

其中一些可以很容易地检查客户端,一些需要服务器端检查。

什么是最好的方法 ,或许类似于DataAnnotationsvalidation, 在这种情况下向用户提供警告? 这里的关键是,用户必须能够覆盖警告,并仍然提交表单(或重新提交表单,具体取决于实施)。

想到的最可行的解决scheme是创build一些属性,类似于一个CustomValidationAttribute ,可能会使AJAX调用,并会显示一些警告文本,但不会影响ModelState 。 预期的用法是这样的:

 [WarningOnFieldLength(MaxLength = 150)] [WarningOnPossibleDuplicate()] public string Name { get; set; } 

在看法:

 @Html.EditorFor(model => model.Name) @Html.WarningMessageFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) 

那么,有什么想法?

整体devise

首先,如果用户select忽略这些警告,我相信你将不得不跟踪。 一个简单而透明的方法是有一个Ignore Warnings(忽略警告)checkbox,在提交之前用户必须检查。 另一种select是让他们提交表格两次,而忽略第二次提交的警告; 那么你可能需要一个IgnoreWarnings隐藏字段。 可能有其他的devise,但为了简单起见,我会select第一个选项。

总之,这个方法就是创造

  • 支持validation警告types的所有视图模型的自定义数据注释属性;
  • 视图模型将inheritance的已知基类;
  • 我们将不得不在JavaScript中为每个自定义属性复制逻辑。

请注意,下面的代码只是说明了方法,我不得不承担很多事情,而不知道完整的上下文。

查看模型

在这种情况下,最好将视图模型从实际模型中分离出来,这是一个好主意。 一种可能的方法是为所有支持警告的视图模型提供一个基类:

 public abstract class BaseViewModel { public bool IgnoreWarnings { get; set; } } 

模型需要分开的关键原因是将IgnoreWarnings属性存储在数据库中没什么意义。

您的派生视图模型将如下所示:

 public class YourViewModel : BaseViewModel { [Required] [StringLengthWarning(MaximumLength = 5, ErrorMessage = "Your Warning Message")] public string YourProperty { get; set; } } 

StringLengthWarning是服务器和客户端validation的自定义数据注释属性。 它只是支持最大长度,可以很容易地扩展与任何其他必要的属性。

数据注释属性

该属性的核心是IsValid(value, validationContext方法。

 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)] public class StringLengthWarningAttribute : ValidationAttribute, IClientValidatable { public int MaximumLength { get; set; } public override bool IsValid(object value) { return true; } protected override ValidationResult IsValid(object value, ValidationContext validationContext) { var model = validationContext.ObjectInstance as BaseViewModel; var str = value as string; if (!model.IgnoreWarnings && (string.IsNullOrWhiteSpace(str) || str.Length > MaximumLength)) return new ValidationResult(ErrorMessage); return base.IsValid(value, validationContext); } public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { yield return new StringLengthWarningValidationRule(MaximumLength, ErrorMessage); } } 

该属性实现IClientValidatable并使用自定义客户端validation规则:

 public class StringLengthWarningValidationRule : ModelClientValidationRule { public StringLengthWarningValidationRule(int maximumLength, string errorMessage) { ErrorMessage = errorMessage; ValidationType = "stringlengthwarning"; ValidationParameters.Add("maximumlength", maximumLength); ValidationParameters.Add("ignorewarningsfield", "IgnoreWarnings"); } } 

客户端JavaScript

最后,要使其工作,您需要从视图中引用以下JavaScript:

 $(function () { $.validator.addMethod('stringlengthwarning', function (value, element, params) { var maximumlength = params['maximumlength']; var ignorewarningsfield = params['ignorewarningsfield']; var ctl = $("#" + ignorewarningsfield); if (ctl == null || ctl.is(':checked')) return true; return value.length <= maximumlength; }); $.validator.unobtrusive.adapters.add("stringlengthwarning", ["maximumlength", "ignorewarningsfield"], function (options) { var value = { maximumlength: options.params.maximumlength, ignorewarningsfield: options.params.ignorewarningsfield }; options.rules["stringlengthwarning"] = value; if (options.message) { options.messages["stringlengthwarning"] = options.message; } }); }(jQuery)); 

JavaScript做了一些假设,你可能想重新访问(checkbox名称等)。

更新:HTML助手

要单独显示validation​​消息的错误和警告,需要一些辅助工具。 以下课程提供了一个示例:

 public static class MessageHelpers { public static MvcHtmlString WarningMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) { if (htmlHelper.ViewData.ModelState["IgnoreWarnings"] != null) return htmlHelper.ValidationMessageFor(expression); return MvcHtmlString.Empty; } public static MvcHtmlString ErrorMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) { if (htmlHelper.ViewData.ModelState["IgnoreWarnings"] == null) return htmlHelper.ValidationMessageFor(expression); return MvcHtmlString.Empty; } } 

在看来,他们可以照常使用:

  @Html.EditorFor(model => model.YourProperty) @Html.ErrorMessageFor(model => model.YourProperty) @Html.WarningMessageFor(model => model.YourProperty) 

只是快速评论你提到的可能的重新提交实施…

对于“你是否打算这么做?” validationtypes,从用户的angular度来看,不得不重新提交一个表格,假设他们犯了一个错误可能是非常烦人的。 我只会实现这个“伪validation”在客户端与JavaScript和(希望快速)Ajax调用,如果你不得不击中服务器。

我也会尝试在input的blur / change事件上显示警告,以便在用户点击提交之前显示。 在所有情况下可能都不实用,但我只是想把它扔到那里。

你可以使用jQueryvalidation的depends函数来简化你的生活。

防爆。

 @Html.LabelFor(m => m.UserName) @Html.TextBoxFor(m => m.UserName) @Html.ValidationMessageFor(m => m.UserName) <label>Ignore Warnings</label> <input id="ignore-warnings" type="checkbox" /> <script> $(function () { $("#UserName").rules("add", { minlength: { param: 6, depends: function (element) { return !$("#ignore-warnings").attr('checked'); } }, // server side remote validation for duplicate check remote: { param: '/account/duplicate', depends: function (element) { return !$("#ignore-warnings").attr('checked'); } } }); }); </script> 

这只是一个可能的解决scheme的草图。 有很多添加自定义属性(包括以上)的例子,所以我会跳过这一点。

可以在jQueryvalidation器函数中添加ignore的使用。

然后使用

 $("form").validate({ ignore: ".warning-only" }); 

并在第一次通过validation器之后使用客户端validation器添加“仅警告”类。 这应该允许表单被发送到服务器。

正如我所说,只是一个素描,但这是我一直在研究的用途。

这是一种不用编写任何服务器端代码就能做出警告的方法。 如果元素有这个类(如果它有类,它意味着表单被提交了一次),在你的自定义validation方法中添加类“ignore-validation”到期望的无效元素。 您还需要从#IdOfInput模糊或更改中删除“忽略validation”类,具体取决于它的控件types,该位代码不在此处显示:

 <script type="text/javascript"> $.validator.addMethod('isValidCustomMethod', function (value, element) { if($(element).hasClass('ignore-validation')){ return true; } var isValid = false; //your code to do validation would actually go here return isValid; }); $(document).ready(function () { $('#IdOfInput').rules('add', { isValidCustomMethod: true, messages: { isValidCustomMethod: 'Your warning message here'} }); $('form').submit(function () { $(this).validate().invalidElements().each(function () { if($(this).attr('id')=='IdOfInput'){ $(this).addClass('ignore-validation'); } }); }); }); </script>