使用注释和自定义属性时的验证顺序

时间:2011-05-12 23:33:54

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

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

为了表明我的意思,假装我有这样的形式:

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

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

这是代码:

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 Validator(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数据注释做到这一点,但是我希望将来推出自己的自定义验证器。

谢谢!

3 个答案:

答案 0 :(得分:4)

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

由于您只匹配正则表达式,因此您可以尝试这样做(将使用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。

祝你好运!

答案 1 :(得分:3)

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

如果你想走简单路线,请在web.config中禁用客户端验证和不引人注目的javascript,如下所示:

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

您的页面应该按照您的预期运行,但现在所有验证都将在服务器上进行。如果你想让不引人注目的客户端验证成为一种旋风,那么这些链接应该会有所帮助。

答案 2 :(得分:3)

您需要添加将在客户端运行的验证的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");
});