Validation Procedure Using Annotations and Custom Attributes

I noticed that when creating a custom validation attribute, my validation only works after writing annotations of my own MVC data. Can it work "simultaneously"?

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

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

So, I have an annotation [Required] for all 3, but also, for the Zip property, I have a custom attribute. If the user does NOT enter a first or last name, but enters an invalid Zip (which should confirm my attribute), all three should have an error message, but no. There is only error firstName and 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):

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

In addition, I know that I can do this with a Regexp data annotation, but in the future I will look for my own validators.

Thanks!

+6
source share
3 answers

You need to add the Javascript version of your check, which will be run on the client side (or disable the check on the client side, but a little nafar).

Here is a sample custom validation for email addresses:

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

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

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

And 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
source

There is a better solution than disabling the unobtrusive client check.

Since you use only regex, you can try to do this (will work with JavaScript validation):

 [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:

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

What a pleasure to do so, you can specify your own default error messages!

Oddly enough, some validation attributes (StringLength, Range, RegularExpression) still use AttributeAdapters, while other attributes like CompareAttribute use IClientValidatable.

Good luck

+4
source

The reason this happens is because you have unobtrusive client-side validation and your own validation attribute does not implement IClientValidatable . To do this, you need to implement this to provide the data-* attributes that are needed as part of the client validation process. You will also need to provide a client-side regular expression validation procedure that reflects your server-side validation.

If you want to switch to a simple route, disable client-side validation and unobtrusive javascript in the web.config file as follows:

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

Then your page will behave as you would expect, but all your checking will now take place on the server. If you want to give an unobtrusive client-side swirl check, then these links should be useful.

+3
source

Source: https://habr.com/ru/post/887983/


All Articles