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

时间:2022-09-26 19:13:14

I've noticed that while creating a custom validation attribute, my validation only fires after native MVC data annotations fire. Is there any way it could fire "at the same time"?

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

To show what I mean, pretend I have this form:

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

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

So I have a [Required] annotation for all 3, but in addition, for the Zip property, I have a custom attribute. If the user DOESN'T enter a firstname or lastname, but enters an invalid Zip (which my attribute should validate this), there should be an error message on all three - but there isn't. There's only an error on firstName and lastName.

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

This is the code:

这是代码:

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

Controller:

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

View:

@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):

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

Also, I know I can do this with Regexp data annotation, but I'm looking to roll my own custom validators in the future.

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

Thanks!

3 个解决方案

#1


3  

You need to add a Javascript version of your validation that will run client-side (or disable client-side validation, but that's a bit naff).

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

There's a sample of building custom validation for email addresses here:

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

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

This shows the C# code (which includes setting the javascript function name that will do the client-side validation) as well as the javascript "validemail" routine.

这显示了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"
        };
    }
}

And the JS:

和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");
});

#2


4  

There's a better solution than disabling unobtrusive client validation.

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

Since you're only matching a regular expression, you might try doing this instead (will work with javascript validation):

由于您只匹配正则表达式,因此您可以尝试执行此操作(将使用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.";
    }
}

and in Global.asax:

在Global.asax中:

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

What's nice about doing it this way, you can specify your own default Error Messages!

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

Weird enough, some of the validation attributes (StringLength, Range, RegularExpression) still use AttributeAdapters, while other attributes such as the CompareAttribute uses the IClientValidatable.

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

Good luck!

#3


3  

The reason this is happening is because you have unobtrusive client side validation enabled, and your custom validation attribute doesn't implement IClientValidatable. It would need to implement this to allow the rendering of data-* attributes which are needed as part of the client validation process. You would also need to provide a client side regex validation routine that mirrors you server side validation.

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

If you want to go the easy route, disable client side validation and unobtrusive javascript in web.config like so:

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

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

Your page should then behave how you would expect, but all your validation will now occur on the server. If you want to give the unobtrusive client side validation a whirl, then these links should helpful.

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

#1


3  

You need to add a Javascript version of your validation that will run client-side (or disable client-side validation, but that's a bit naff).

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

There's a sample of building custom validation for email addresses here:

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

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

This shows the C# code (which includes setting the javascript function name that will do the client-side validation) as well as the javascript "validemail" routine.

这显示了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"
        };
    }
}

And the JS:

和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");
});

#2


4  

There's a better solution than disabling unobtrusive client validation.

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

Since you're only matching a regular expression, you might try doing this instead (will work with javascript validation):

由于您只匹配正则表达式,因此您可以尝试执行此操作(将使用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.";
    }
}

and in Global.asax:

在Global.asax中:

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

What's nice about doing it this way, you can specify your own default Error Messages!

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

Weird enough, some of the validation attributes (StringLength, Range, RegularExpression) still use AttributeAdapters, while other attributes such as the CompareAttribute uses the IClientValidatable.

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

Good luck!

#3


3  

The reason this is happening is because you have unobtrusive client side validation enabled, and your custom validation attribute doesn't implement IClientValidatable. It would need to implement this to allow the rendering of data-* attributes which are needed as part of the client validation process. You would also need to provide a client side regex validation routine that mirrors you server side validation.

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

If you want to go the easy route, disable client side validation and unobtrusive javascript in web.config like so:

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

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

Your page should then behave how you would expect, but all your validation will now occur on the server. If you want to give the unobtrusive client side validation a whirl, then these links should helpful.

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