49

有时用户输入并非严格无效,但可能被认为是有问题的。

例如:

  • 用户在单行Name字段中输入长句子。他可能应该改用这个Description领域
  • 用户输入的输入Name与现有实体的输入非常相似。也许他正在输入相同的实体,但没有意识到它已经存在,或者某个并发用户刚刚输入了它。

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

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

想到的最可行的解决方案是创建一些类似于 a 的属性,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)

那么,有什么想法吗?

4

5 回答 5

51

整体设计

首先,我相信如果用户选择忽略警告,您将不得不以某种方式进行跟踪。一种简单而透明的方法是设置一个忽略警告复选框,用户必须在提交之前检查该复选框。另一种选择是让他们提交表单两次并忽略第二次提交时的警告;那么你可能需要一个IgnoreWarnings隐藏字段。可能还有其他设计,但为了简单起见,我将使用第一个选项。

简而言之,方法是创建

  • 支持警告类型验证的所有视图模型的自定义数据注释属性;
  • 视图模型将从其继承的已知基类;
  • 我们必须为每个自定义属性复制 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是用于服务器和客户端验证的自定义数据注释属性。它只支持最大长度,并且可以使用任何其他必要的属性轻松扩展。

数据注释属性

属性的核心是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并利用自定义客户端验证规则:

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 做了一些您可能想要重新访问的假设(复选框名称等)。

更新:HTML 助手

要分别显示错误和警告的验证消息,需要几个帮助程序。下面的类提供了一个示例:

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)
于 2012-11-13T11:16:00.947 回答
4

您可以使用dependsjquery 验证的功能来简化您的生活。

前任。

@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>
于 2012-11-13T14:21:55.103 回答
3

只是对您提到的可能的重新提交实施的快速评论......

对于“你的意思是这样做吗?” 验证类型,从用户的角度来看,必须基于他们犯了错误的假设重新提交表单可能非常烦人。如果您必须访问服务器,我只会在客户端使用 javascript 和(希望是快速的)ajax 调用来实现这个“伪验证”。

我还将尝试在输入的模糊/更改事件上显示警告,以便它们在用户点击提交之前显示。也许在所有情况下都不实用,但我只是想我会把它扔在那里。

于 2012-11-10T07:22:55.037 回答
1

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

可以在jQuery 验证器函数中添加忽略的使用。

然后使用

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

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

正如我所说,只是一个草图,但这是我一直在研究以备将来使用的东西。

于 2012-11-13T17:01:51.500 回答
0

这是一种无需编写任何服务器端代码即可发出警告的方法。将类“ignore-validation”添加到表单提交时所需的无效元素,如果元素具有此类,则在您的自定义验证方法中返回“true”(如果它具有类,则表示表单已提交一次)。您还需要在模糊或更改时从#IdOfInput 中删除“ignore-validation”类,具体取决于控件的类型,此处未表示该位代码:

<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>
于 2013-04-09T19:08:26.000 回答