I recently worked on a rWhois project that required writing an ASP.NET MVC 4 based admin portal. One of the last things I tackled was adding validation to inputs. Adding validation to a MVC model is super-simple with Data Annotations. By adding these annotations to your model, both client and server-side validation comes to life. There’s a great tutorial describing how to accomplish this on the asp.net site:
The in-box DataAnnotations class provides numerous built-in validation attributes that can be applied to any property. Things like strings of specific length, or integers within a range, or even regex matching is supported using the built-in validation attributes – and they are automatically wired up when you use Visual Studio to add views with scaffolding. But what about when the built-in attributes don’t quite do what you need?
I needed to create validation that confirms the input is both in proper CIDR format (ie. 10.0.0.0/8 and not “some text”) and that the supplied CIDR subnet is valid (ie. 192.168.100.0/24 is a proper network/bitmask, but 192.168.100.50/24 is not). Validating that the input is in the proper format can easily be done with regex. Just add the following to annotation to the appropriate property on your model:
[RegularExpression(@"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(\d|[1-2]\d|3[0-2]))$", ErrorMessage="Not valid CIDR format")]
To check that the supplied input is a valid network/bitmask CIDR combination, I needed custom validation. You can easily extend the ValidationAttribute and implement IClientValidatable to add your custom validation. First, you’ll want to create a new class and reference the DataAnnotations and Mvc namespaces:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace MvcApplication1.Validation
{
public class CIDRSubnet : ValidationAttribute, IClientValidatable
{
}
}
Next, we’ll want to create the appropriate server-side validation by overriding the ValidationResult class and adding the CIDR subnet check:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//validate that valus is valid cdir
string[] cidr = ((string)value).Split(new char[] { '/' });
string[] ip = cidr[0].Split(new char[] { '.' });
int i = (Convert.ToInt32(ip[0]) << 24) | (Convert.ToInt32(ip[1]) << 16) | (Convert.ToInt32(ip[2]) << 8) | (Convert.ToInt32(ip[3]));
int mask = Convert.ToInt32(cidr[1]) == 0 ? 0 : ~((1 << (32-29)) - 1);
int network = i & mask;
if (i != network) return new ValidationResult("Not a valid CIDR Subnet");
return ValidationResult.Success;
}
Now we want to create our client side validation function. Create a new javascript file and make sure it’s referenced in your View:
function ipv4checker(input) {
//confirm cidr is valid network
var cidr = input.split('/');
var base = cidr[0];
var bits = cidr[1];
var ip = base.split('.');
var i = (ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]);
var mask = bits == 0 ? 0 : ((1 << (32 - bits)) - 1) ^ 0xFFFFFFFF;
var network = i & mask;
if (i == network) { return true; }
else { return false; }
}
Assuming jquery and jquery unobtrusive validation libraries have already been added to your project, you can then add a method to the validator, and wire it up. I’m not passing any parameters here, but you could also include parameters:
//custom vlidation rule - check IPv4
$.validator.addMethod("checkcidr",
function (value, element, param) {
return ipv4checker(value);
});
//wire up the unobtrusive validation
$.validator.unobtrusive.adapters.add
("checkcidr", function (options) {
options.rules["checkcidr"] = true;
options.messages["checkcidr"] = options.message;
});
Then, you’ll need to implement the GetClientValidationRules method in your custom validation class:
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule rule = new ModelClientValidationRule();
rule.ValidationType = "checkcidr";
rule.ErrorMessage = "Not a valid CIDR Subnet";
return new List<ModelClientValidationRule> { rule };
}
Lastly, add a reference to your custom validation class in the model and add the custom validation attribute to your property:
using System.ComponentModel.DataAnnotations;
using MvcApplication1.Validation;
namespace MvcApplication1.Models
{
public class Allocation
{
[Required]
[RegularExpression(@"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(\d|[1-2]\d|3[0-2]))$", ErrorMessage="Not valid CIDR format")]
[CIDRSubnet(ErrorMessage="Not a valid CIDR Subnet")]
public string IPNetwork { get; set; }
}
}