简体   繁体   中英

Server Side Custom Data Annotation Validation MVC 3

I have been trying to make an EmailAddress custom data annotation attribute properly validate emails, and it doesn't seem to be working for me in my tests.

Here's the test:

    [Test]
    public void ModelStateValidation()
    {
        var invalidEmails = new[] { "testtestcom", "test@testcom", "testtest.com" };
        var validEmail = "test@test.com";

        //Invalid Email addresses 
        foreach (var invalidEmail in invalidEmails)
        {
            var subscription = new SubscriberModel() { FirstName = "f", LastName = "l", Email = invalidEmail, ConfirmEmail = invalidEmail, DateSubscribed = DateTime.Now };
            ValidateModelAsFalse(subscription);
        }

        var subscription2 = new SubscriberModel() { FirstName = "f", LastName = "l", Email = "t", ConfirmEmail = "t", DateSubscribed = DateTime.Now };
        ValidateModelAsFalse(subscription2);

        //Valid Email address
        var subscription3 = new SubscriberModel() { FirstName = "f", LastName = "l", Email = validEmail, ConfirmEmail = validEmail, DateSubscribed = DateTime.Now };
        ValidateModelAsTrue(subscription3);

        //Last Name doesn't exists
        var subscription4 = new SubscriberModel() { FirstName = "f", LastName = "", Email = validEmail, ConfirmEmail = validEmail, DateSubscribed = DateTime.Now };
        ValidateModelAsFalse(subscription4);

        //First Name doesn't exists
        var subscription5 = new SubscriberModel() { FirstName = "", LastName = "l", Email = validEmail, ConfirmEmail = validEmail, DateSubscribed = DateTime.Now };
        ValidateModelAsFalse(subscription5);

        //Email addresses are equal
        var subscription6 = new SubscriberModel() { FirstName = "f", LastName = "l", Email = "test@test.com", ConfirmEmail = "test2@test.com", DateSubscribed = DateTime.Now };
        ValidateModelAsFalse(subscription6);
    }

    private static void ValidateModelAsFalse(SubscriberModel subscription3)
    {
        SubscriptionController controller = new SubscriptionController();
        controller.ValidateModel(subscription3);
        AssertIsFalse(controller);
    }

    private static void ValidateModelAsTrue(SubscriberModel subscription3)
    {
        SubscriptionController controller = new SubscriptionController();
        controller.ValidateModel(subscription3);
        AssertIsTrue(controller);
    }

    private static void AssertIsTrue(SubscriptionController controller)
    {
        Assert.That(controller.ModelState.IsValid, Is.True);
    }

    private static void AssertIsFalse(SubscriptionController controller)
    {
        Assert.That(controller.ModelState.IsValid, Is.False);
    }

Here's the ValidateModel method:

<!-- language: c# -->
public static void ValidateModel<T>(this Controller controller, T model)
    {
        var validationContext = new ValidationContext(model, null, null);
        var validationResults = new List<ValidationResult>();
        Validator.TryValidateObject(model, validationContext, validationResults);
        foreach (var validationResult in validationResults)
        {
            controller.ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage);
        }
    }

Model:

<!-- language: c# -->
public partial class SubscriberModel
{
    public int SubscriberID { get; set; }

    [Required]
    [DisplayName("First Name")]
    public string FirstName { get; set; }

    [Required]
    [DisplayName("Last Name")]
    public string LastName { get; set; }

    [Required]
    [EmailAddress]
    [Compare("ConfirmEmail", ErrorMessage = "The emails must match")]
    [DisplayName("E-mail")]
    public string Email { get; set; }

    [Required]
    [DisplayName("Confirm E-mail")]
    public string ConfirmEmail { get; set; }

    [Required]
    [DisplayName("Date Subscribed")]
    public DateTime DateSubscribed { get; set; }
}

Controller:

    public partial class SubscriptionController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [CaptchaValidator]
    [HttpPost]
    public ActionResult Index(SubscriberModel subscription, bool captchaValid)
    {
        subscription.DateSubscribed = DateTime.Now;
        ModelStateValid = ModelState.IsValid;

        if(captchaValid)
        {
            if (ModelState.IsValid)
            {
                var test = "";
            }
        }

        return View(subscription);
    }
}

EmailAttribute class:

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public sealed class EmailAddressAttribute : ValidationAttribute, IClientValidatable
{ 
    private static Regex _regex = new Regex(@"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

    public EmailAddressAttribute()
        : base()
    {
        ErrorMessage = "Invalid Email";
    }

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

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

        string valueAsString = value as string;
        return _regex.Match(valueAsString).Success;
    }
}

The IsValid method seems to validate email addresses as I intend, but for some reason when I go to run the unit test on the Controller.ValidateModel, it doesn't return false and match my Assertion.

I used something like this once and implementing the IClientValidatable interface was just the first step. then I had to add jQuery validation on the client side. I think that custom validations can only be executed on the client using jQuery

here is an example:

<script type="text/javascript">
    (function ($) {
        $.validator.addMethod("yourValidationTypeName", function (value, element) {
            //do validation here
        });
        $.validator.unobtrusive.adapters.addBool("yourValidationTypeName");
    } (jQuery));
</script>

Using the built in attribute [Regularexpression] with unobtusive javascript and client validation enabled might solve your problem.

Correct me if I'm wrong, but I'm not an expert with MVC3

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