【问题标题】:Order of Validation When Using Annotations and Custom Attributes使用注释和自定义属性时的验证顺序
【发布时间】:2011-05-12 23:33:54
【问题描述】:

我注意到,在创建自定义验证属性时,我的验证仅在本机 MVC 数据注释触发后触发。有什么办法可以“同时”开火?

为了表达我的意思,假装我有这个表格:

FirstName: <FirstName Textbox>
LastName: <LastName TextBox>
Zip: <Zip TextBox>

所以我对所有 3 个都有一个 [Required] 注释,但此外,对于 Zip 属性,我有一个自定义属性。如果用户没有输入名字或姓氏,但输入了无效的 Zip(我的属性应该对此进行验证),则所有三个都应该有一条错误消息 - 但没有。名字和姓氏只有一个错误。

这是代码:

Person.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

// My validator
using MvcApplication3.Extensions.Validation;

namespace MvcApplication3.Models
{
  public class Person
  {
    [Required(ErrorMessage="Field required!")]
    public string firstName{get;set;}

    [Required(ErrorMessage="Field required!")]
    public string lastName { get; set; }    

    [Zip(ErrorMessage="You gotta put in a valid zip code")]
    [Required(ErrorMessage="Field required!")]
    public string zipCode { get; set; }    
  }
}

控制器:

[HttpPost]
public ActionResult Index(FormCollection form, Person person)
{
  return View(person);
}  

查看:

@model MvcApplication3.Models.Person
@{
  ViewBag.Title = "Person";
  Layout = "~/Views/Shared/_Layout.cshtml";       

}
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

<h2>
  Testing Form: @Model.firstName
</h2>
<hr />

@{Html.EnableClientValidation();}

@using (Html.BeginForm())
{ 
  @Html.LabelFor(model => model.firstName) 
  @Html.TextBoxFor(model => model.firstName) 
  @Html.ValidationMessageFor(model=>model.firstName)

  <br /><br />
  @Html.LabelFor(model => model.lastName) 
  @Html.TextBoxFor(model => model.lastName) 
  @Html.ValidationMessageFor(model=>model.lastName)

  <br /><br />
  @Html.LabelFor(model => model.zipCode) 
  @Html.TextBoxFor(model => model.zipCode) 
  @Html.ValidationMessageFor(model=>model.zipCode)    

  <br /><br />
  <input type="submit" value="Submit" />
}

Zip 验证器 (Zip.cs):

  public class ZipAttribute : ValidationAttribute
  {
    public override bool IsValid(object value)
    {
      bool foundMatch = false;
      try
      {
        foundMatch = Regex.IsMatch(value.ToString(), "\\A\\b[0-9]{5}(?:-[0-9]{4})?\\b\\z");
      }
      catch (ArgumentException ex)
      {
        // Syntax error in the regular expression
      }
      return foundMatch;
    }
  }

另外,我知道我可以使用 Regexp 数据注释来做到这一点,但我希望将来推出自己的自定义验证器。

谢谢!

【问题讨论】:

    标签: c# asp.net-mvc asp.net-mvc-3 razor


    【解决方案1】:

    有比禁用不显眼的客户端验证更好的解决方案。

    由于您只匹配正则表达式,您可以尝试这样做(将与 javascript 验证一起使用):

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
    public class ZipAttribute : System.ComponentModel.DataAnnotations.RegularExpressionAttribute
    {
        public ZipAttribute() : base("\\A\\b[0-9]{5}(?:-[0-9]{4})?\\b\\z")
        {
            ErrorMessage = "Invalid ZIP code.";
        }
    }
    

    在 Global.asax 中:

            DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(ZipAttribute), typeof(RegularExpressionAttributeAdapter));
    

    这样做有什么好处,您可以指定自己的默认错误消息!

    很奇怪,一些验证属性(StringLength、Range、RegularExpression)仍然使用 AttributeAdapters,而其他属性(例如 CompareAttribute)使用 IClientValidatable。

    祝你好运!

    【讨论】:

    • 感谢您的反馈。是的,这就是我正在寻找的东西,但我想使用自定义 C# 逻辑来验证,而不是正则表达式。除了从 RegularExpressionAttribute 继承,我可以从某个可以放入自己的验证逻辑代码的类继承吗?
    • 如果您要使用自定义代码,您仍然需要编写客户端 Javascript。没有将 C# 代码自动转换为 Javascript 代码。您必须自己完成这项工作。
    • 为什么有些属性使用 IClientValidatable 而有些不使用的“奇怪”是因为那些随 .NET 框架(必需和朋友)一起发布的属性,他们不了解 MVC。 IClientValidatable 接口是 MVC 接口,而不是 .NET 接口。 CompareAttribute 与 MVC 一起提供,因此它知道(并且可以实现)IClientValidatable。
    • 对不起,如果我在这里玩游戏有点晚了。感谢所有的回复——他们都很有道理。尽管对我来说,硬塞客户端验证似乎有点痛苦。由于在其中插入 jQuery 验证代码似乎更容易,独立于 C# 代码 - 这样做有什么好处吗?也就是说,为什么我要从 IClientValidatable 继承并添加额外的 C# 逻辑,而我仍然必须在 JavaScript 中实现验证逻辑?似乎没有任何好处。
    【解决方案2】:

    您需要添加将在客户端运行的验证的 Javascript 版本(或禁用客户端验证,但这有点不合时宜)。

    这里有一个为电子邮件地址构建自定义验证的示例:

    http://thepursuitofalife.com/asp-net-mvc-3-unobtrusive-javascript-validation-with-custom-validators/

    这显示了 C# 代码(包括设置将执行客户端验证的 javascript 函数名称)以及 javascript“validemail”例程。

    public class ValidEmailAttribute : ValidationAttribute, IClientValidatable
    {
        // ...
    
        public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            yield return new ModelClientValidationRule
            {
                ErrorMessage = FormatErrorMessage(metadata.DisplayName),
                ValidationType = "validemail"
            };
        }
    }
    

    还有 JS:

    $(function() {
        jQuery.validator.addMethod("validemail", function (value, element, param) {
            var emailPattern = /^[a-zA-Z0-9._-]+@@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
            return emailPattern.test(value);
        });
        jQuery.validator.unobtrusive.adapters.addBool("validemail");
    });
    

    【讨论】:

      【解决方案3】:

      发生这种情况的原因是您启用了不显眼的客户端验证,并且您的自定义验证属性未实现 IClientValidatable。它需要实现这一点以允许呈现data-* 属性,这些属性是客户端验证过程的一部分。您还需要提供一个反映服务器端验证的客户端正则表达式验证例程。

      如果您想走简单的路线,请在 web.config 中禁用客户端验证和不显眼的 javascript,如下所示:

      <appSettings>
          <add key="ClientValidationEnabled" value="false"/> 
          <add key="UnobtrusiveJavaScriptEnabled" value="false"/> 
      </appSettings>
      

      然后,您的页面应该按照您的预期运行,但是您的所有验证现在都将在服务器上进行。如果您想试一试不显眼的客户端验证,那么这些链接应该会有所帮助。

      【讨论】:

      • 感谢您的反馈。所以我几乎必须编写与我的验证器匹配的等效 JavaScript 代码?
      • 很遗憾你这样做了 - 请参阅下面我的答案中的链接。您可以使用 Script# 来做到这一点,但是没有任何内置功能可以将您的 C# 转换为 Javascript(但这就是 Script# 的目的:-))
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-13
      • 1970-01-01
      • 2014-11-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多