:::: MENU ::::

Wednesday, February 8, 2012

This post will demonstrate how to do rock-solid phone number validation using the .NET port of Google'slibphonenumber. When validating phone numbers you tend to end up either frustrating users with strict input requirements or with complicated regexes to cover all the interesting ways that users input phone numbers. If you have to also check international numbers (as we do at AppHarbor) the task becomes almost impossible.

Enter libphonenumber, a library from Google containing years of accumulated wisdom on how to parse phone numbers from all over the world. Patrick Mézard has created a .NET port of the Java version and there is even a NuGet package.

This makes using libphonenumber for validation as easy as installing the NuGet package and wrapping it in aDataAnnotation attribute like the one below (or just use the library straight up). The Attribute below will parse US numbers with and without country code while international numbers require a country code. Example of use:

 using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using MyNamespace.DataAnnotations;

public class Address
{
   
[DisplayName("Phone number")]
   
[Editable(allowEdit: true)]
   
[PhoneNumberAttribute]
   
[Required]
   
public virtual string PhoneNumber
   
{
       
get;
       
set;
   
}
}

Attribute:

 using System.ComponentModel.DataAnnotations;
using PhoneNumbers;

namespace MyNamespace.DataAnnotations
{
   
public class PhoneNumberAttribute : ValidationAttribute
   
{
       
public override bool IsValid(object value)
       
{
           
var valueString = value as string;
           
if (string.IsNullOrEmpty(valueString))
           
{
               
return true;
           
}

           
var util = PhoneNumberUtil.GetInstance();
           
try
           
{
               
var number = util.Parse(valueString, "US");
               
return util.IsValidNumber(number);
           
}
           
catch (NumberParseException)
           
{
               
return false;
           
}
       
}
   
}
}

Unit tests for attribute:

 using MyNamespace.DataAnnotations;
using Xunit;
using Xunit.Extensions;

namespace MyNamespace.UnitTest.DataAnnotations
{
   
public class PhoneNumberAttributeTest
   
{
       
private readonly PhoneNumberAttribute _subjectUnderTest;

       
public PhoneNumberAttributeTest()
       
{
            _subjectUnderTest
= new PhoneNumberAttribute();
       
}

       
[Fact]
       
public void GivenNull_WhenValidate_ThenIsValid()
       
{
           
Assert.True(_subjectUnderTest.IsValid(null));
       
}

       
[Fact]
       
public void GivenEmptyString_WhenValidate_ThenIsValid()
       
{
           
Assert.True(_subjectUnderTest.IsValid(string.Empty));
       
}

       
[InlineData("+4527122799")]
       
[InlineData("6503181051")]
       
[InlineData("+16503181051")]
       
[InlineData("1-650-318-1051")]
       
[InlineData("+1-650-318-1051")]
       
[InlineData("+1650-318-1051")]
       
[Theory]
       
public void GivenValidPhoneNumber_WhenValidate_ThenIsValid(string phoneNumberString)
       
{
           
Assert.True(_subjectUnderTest.IsValid(phoneNumberString));
       
}

       
[InlineData("123")]
       
[InlineData("foo")]
       
[Theory]
       
public void GivenInValidPhoneNumber_WhenValidate_ThenIsNotValid(string phoneNumberString)
       
{
           
Assert.False(_subjectUnderTest.IsValid(phoneNumberString));
       
}
   
}
}
 More