Monday, August 27, 2018

RequiredEither - Conditional Validation Attribute using MVC / Web API

This topic illustrates how to extend ValidationAttribute to enforce customized validation of checking if either of the field's value is provided or not.

Scenario: Contact.Phone or Contact.Email value should be required.

We can achieve this by following below steps, and the illustration is developed using MVC 5, Web API 2, EF 6 and Moq

As a first step, Create a new class with name "RequiredEitherValidator" (preferably in a common location of the solution ie. either in Model or a Common project, as applied) and copy below code:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class RequriedEitherValidator: ValidationAttribute
{
private string[] PropertyList { get; set; }

public RequriedEitherValidator(params string[] propertyList)
{
this.PropertyList = propertyList;
}

public override object TypeId
{
get { return this; }
}

public override bool IsValid(object value)
{
PropertyInfo propertyInfo;
foreach (string propertyName in PropertyList)
{
propertyInfo = value.GetType().GetProperty(propertyName);
if (propertyInfo != null && propertyInfo.GetValue(value, null) != null)
{
return true;
}
}
return false;
}
}
We need to create or make changes to Model(s) and Controller(s) as below to implement the created custom validation.

POCO / Model: Create or Modify Contact class to apply the custom validation on Phone and Email properties as below to define either of the field value is required.
[RequriedEitherValidator("Email", "Phone", ErrorMessage = "Either Email or Phone Data is Required")]
public class Contact
{
[Display(Name="First Name")]
[StringLength(50, ErrorMessage = "First Name cannot Exceed 50 Character length")]
[Required(ErrorMessage = "First Name is Required")]
public string FirstName {get; set; }

[Display(Name="Last Name")]
[StringLength(50, ErrorMessage = "Last Name cannot Exceed 50 Character length")]
[Required(ErrorMessage = "Last Name is Required")]
public string LastName { get; set; }

[StringLength(150)]
[RegularExpression("^[a-zA-Z0-9_\\.-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$", ErrorMessage = "Invalid Email address")]
public string Email { get; set; }

[RegularExpression(@"^([0-9]{10})$", ErrorMessage = "Invalid Phone Number")]
public string Phone { get; set; }
}
Controller: The controller for Contact entity with a POST method will look like below.
public class ContactController : ApiController
{
[AcceptVerbs("GET", "POST")]
[HttpPost]
[ActionName("CreateContact")]
public bool CreateContact([FromBody]Contact contact)
{
try
{
var isAdded = _contactRepository.CreateContact(contact);
return isAdded;
}
catch (Exception e)
{
throw e;
}
}
}
Testing: Below TestMethods will help testing the post method to check for the expected validation error:
[TestMethod]
public void FailedPhoneOrEmailDetailsTest()
{
Contact contact = new Contact { FirstName = "TestFirstName", LastName = "TestLastName", Phone = null, Email = null };
var validationResult = Validator.TryValidateObject(_Contacts, _ContactsContext, _validationResults, validateAllProperties: true);

Assert.IsFalse(validationResult);
Assert.AreEqual(1, _validationResults.Count);
Assert.AreEqual("Either Email or Phone Data is Required", _validationResults[0].ErrorMessage);
}
}
With this I am concluding the illustration. Feel free to share your feedback.

Happy Programming !!!

1 comment: