2012-11-10 18 views
46

有时用户输入不是严格无效的,但可以认为是有问题的。如何在ASP.NET MVC验证期间提供警告?

例如:

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

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

什么是最好的方法,也许类似于DataAnnotations验证,在这种情况下提供警告给用户?这里的关键是用户必须能够覆盖警告并仍然提交表单(或者根据实施情况重新提交表单)。

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

因此,任何想法?

+0

我假设你想要像'[WarningOnFieldLength(150)]'这样的属性? –

+0

@TiesonT。对,就是这样。 – Alex

+1

像这样的答案可以让你在那里:http://stackoverflow.com/a/9652582/534109 - 假设你想使用强类型助手,该属性可以用来为扩展生成一个钩子。我会看看我是否可以一起刮出一个真正的答案... –

回答

47

总体设计

首先,我相信你会以某种方式跟踪如果用户选择忽略该警告。一个简单而透明的方法就是有一个忽略警告复选框,该用户在提交之前必须检查。另一种选择是让他们提交表格两次,并忽略第二次提交的警告;那么你可能需要一个隐藏字段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提供了一些您可能想要重新访问的假设(复选框名称等)。

UPDATE: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) 
+0

很好的答案,谢谢:) – Alex

+6

真的很棒,非常完整的答案。你应得此赏赐! –

+0

为什么不使用数据注释的很好例子。谢谢! –

3

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

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

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

4

你可以使用jQuery验证的depends功能,以简化你的生活。

Ex。

@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> 
1

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

可能在jQuery validator function中增加使用忽略。

然后使用

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

,并使用客户端验证第一次通过验证之后加入“警告-只有”类。这应该允许将表单发送到服务器。正如我所说,只是一个草图,但这是我一直在研究的用途。

+0

@Alex我看到上面提出了同样的解决方案。将此与Serge Belov的回答结合起来应该可以让你在那里。 –

0

这是一种在不写任何服务器端代码的情况下执行警告的方法。将表单“ignore-validation”添加到表单提交中所需的无效元素,并且在您的自定义验证方法中,如果元素具有此类(如果它具有类,则表示表单已提交一次)返回“true”。您还需要从#IdOfInput删除“忽略验证”级上的模糊或改变,这取决于各种控制它的的代码位没有在这里表示:

<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>