Custom validation attributes

After some discussions with my great colleagues, I felt that it could be a good thing to write a short blog post about custom validation attributes. Maybe you have not used it yet.

In short, validation attributes…are great. For instance, consider these four validation scenarios:

  • E-mail addresses
  • Postal codes
  • Social security numbers
  • URLs

All these validations could (to some extent) be expressed as regular expressions. Let’s start off by looking at four implementations (Swedish context):

public class EmailAddressAttribute : RegularExpressionAttribute
{
   public EmailAddressAttribute()
      : base(@"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$") { }

   public override bool IsValid(object value)
   {
      if (value == null || value.ToString().IsNullOrEmpty())
         return false;
      return base.IsValid(value);
   }
}

public class SwedishPostalCodeAttribute : RegularExpressionAttribute
{
   public SwedishPostalCodeAttribute(bool optionalSpace = false)
      : base(optionalSpace ? "^\\d{3}\\ ?\\d{2}$" : "^\\d{5}$") { }

   public override bool IsValid(object value)
   {
      if (value == null || value.ToString().IsNullOrEmpty())
         return false;
      return base.IsValid(value);
   }
}

public class SwedishSsnAttribute : RegularExpressionAttribute
{
   public SwedishSsnAttribute()
      : base("^\\d{6}-?\\d{4}$") { }

   public override bool IsValid(object value)
   {
      if (value == null || value.ToString().IsNullOrEmpty())
         return false;

      //Remove possible dash
      var noDash = value.ToString().Replace("-", "");

      //Verify the Luhn algorithm
      var sum = 0;
      for (var i = 0; i < 9; i++)
      {
         var tmpInt = int.Parse(noDash[i].ToString());
         tmpInt = tmpInt * (((i + 1) % 2) + 1);
         sum += (tmpInt > 9) ? tmpInt - 9 : tmpInt;
         sum = (sum > 10) ? sum - 10 : sum;
      }

      //Verify the check digit
      return (10 - sum) == int.Parse(noDash[9].ToString());
   }
}

public class UrlAttribute : RegularExpressionAttribute
{
   public UrlAttribute()
      : base(@"^(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?") { }

   public override bool IsValid(object value)
   {
      if (value == null || value.ToString().IsNullOrEmpty())
         return false;
      return base.IsValid(value);
   }
}

As you can see, the attributes for e-mail, postal codes and urls only use the regex (and a null/empty condition), while the social security attribute does a bit more.

The Swedish postal code attribute, however, is insufficient. It should also verify (with some external service perhaps?) that the postal code actually exists, but hey…that requires some out of scope integrations. 🙂

This approach gathers the regex and the actual validation in one place. If one would need to use the regex separately, it can be accessed with the Pattern property. You can then use the regex to provide a shallow validation client-side, while using the attribute to perform a deeper validation server-side.

Advertisements