简体   繁体   中英

MVC 5 custom validation attribute not triggered

So I've been banging my head against the wall here. I followed the tutorials to the letter and I have no ideia why this custom validation isn't working. It is supposed to validate phone numbers.

for shorter go to the end

Custom attribute:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class CustomPhoneAttribute : DataTypeAttribute, IClientValidatable
{
    private static readonly string[] ddds = new[] { ... };

    private static Regex regex = new Regex(@"^(\([0-9]{2}\)) ([0-9]{5}-[0-9]{4}|[0-9]{4}-[0-9]{4}$", RegexOptions.Compiled);

    private const string DefaultErrorMessage = "{0} must be a phone number.";

    public CustomPhoneAttribute()
      : base(DataType.PhoneNumber)
    {
        ErrorMessage = DefaultErrorMessage;
    }

    public override string FormatErrorMessage(string name)
    {
        return string.Format(ErrorMessageString, name);
    }

    private bool ValidatePhone(string phone)
    {

        if (regex.Match(phone).Success)
        {
            var ddd = phone.Substring(1, 2);

            if (ddds.Contains(ddd))
            {
                var phoneParts = phone.Substring(5).Split('-');
            }

            // TODO: perform further evaluation base on
            // ddd, first digit and extra 9.
            // For now we only check the ddd exists.

            return true;
        }

        return false;
    }

    public override  bool IsValid(object value)
    {
        if (value == null)
        {
            return true;
        }

        return ValidatePhone((string)value);
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        yield return new ModelClientValidationRule()
        {
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
            ValidationType = "brphone"
        };

    }

Model:

{...}

[CustomRequired] // this works.
[DataType(DataType.PhoneNumber)]
[Display(Name = "Phone")]
[CustomPhone]
public string Phone { get; set; }

{...}

Javascript:

$.validator.addMethod("brphone",

function (value, element) {

    if (!this.optional(element)) {

        // perform validation.
        return true;
    }

    return true;
});

$.validator.unobtrusive.adapters.addBool("brphone");

View:

@Html.TextBoxFor(m => m.Phone, new { @class = "form-control phonemask-client", placeholder = "(xx) xxxxx-xxxx" })

Everything seems to check out, still neither server side nor client side validation is working.

FIGURED PART OF IT OUT (possibly bug from microsoft)

If I remove the regex object from the data annotation class, it works like a charm. Now, why that happens, I have no idea! If I attach a debugger, when it hits the regex declaration and I click step over to continue debugging, the program just returns to the web page and nothing happens.

Is it forbidden to use regex inside a data annotation?

You're missing a parentheses in your regular expression, which is causing an error when the attribute is instantiated.

// missing ')' between the {4} and $
private static Regex regex = new Regex(@"^(\([0-9]{2}\)) ([0-9]{5}-[0-9]{4}|[0-9]{4}-[0-9]{4})$", RegexOptions.Compiled); 

We don't see an error at runtime due to the static keyword on the field.

Let's pretend that your attribute looked like this:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class CustomPhoneAttribute : DataTypeAttribute
{
    public static string MyString = GetValue();

    public CustomPhoneAttribute()
        : base(DataType.PhoneNumber)
    {
    }

    private static string GetValue()
    {
        throw new Exception();
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        return base.IsValid(value, validationContext);
    }
}

When using the static keyword, the runtime will need to initialize the type-level values at some point in time before the class itself is instantiated. However, when this happens it causes a crash, because the GetValue code is throwing an exception. That error is going to occur when MVC is looking at the model for data annotations, and it will in turn ignore that attribute (since it failed to be instantiated).

By removing the static keyword, you cause the error to be thrown in your constructor when the annotation is being instantiated, so it's first thrown in your code and the debugger can break on it.

Solved. Problem was in the regex. It was missing a closing ")". Ridiculously enough, I did not get an exception for that.

So:

@"^(\([0-9]{2}\)) ([0-9]{5}-[0-9]{4}|[0-9]{4}-[0-9]{4}$"

becomes

@"^(\([0-9]{2}\)) ([0-9]{5}-[0-9]{4}|[0-9]{4}-[0-9]{4})$"

To figure out the exception, I had to implement the annotation in another project. as library. Then, I added the dll to my current project and tried to use it. I got a yellow page saying my regex was wrong.

This exception should have thrown from my own code, meh.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM