ASP.NET MVC 4 Custom Validation for CIDR Subnet

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:

http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-validation-to-the-model

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

Cannot find Newtonsoft.Json error when deploying

We recently revamped our source control, continuous integration, and bug tracking solution at OrcsWeb to take advantage of Git. Part of the solution includes a build server that automatically runs tests against projects and, assuming they pass, packages the and deploys the application using Web Deploy. Several of the projects typically include NuGet packages, and a new feature of NuGet (as of 1.6 & 2.0) allows you to exclude these NuGet packages when committing to source control. By enabling package restore, NuGet will automatically download any packages from packages.config on any system where they’re missing.

After uploading a new MVC 4 project that was using this NuGet Package Restore functionality, I was receiving the YSOD that asp.net “Could not load file or assembly ‘Newtonsoft.Jason, Version=4.5.0.0′”. I confirmed that NuGet was downloading the packages on the build server, but for some reason it wasn’t being included in the deployment. BlackSpy’s solution for the Newtonsoft.Json error on stackoverflow.com fixed the issue for me as well.

I removed the entry for Newtonsoft.Json version 4.5.6 from packages.config, saved and built the project, then re-added Newtonsoft.Json 4.5.11 from NuGet and committed the changes. The build server picked up the updates, downloaded the missing package, and deployed it to the server.

 

Running a MVC 4 application on IIS7

Recently tried to get a new MVC 4 application running on IIS7 and ran into an issue that required a hotfix. The IIS7 server was 2008 SP2 fully patched and had both .NET 2.0 and .NET 4.5 installed, however, whenever I attempted to load the application, IIS would return a 403.14 error message about a directory listing being denied.

Many solutions suggested that ASP.NET wasn’t registered properly, and I confirmed that my application pool settings were correct (need to run Integrated Mode or configure asp.net to handle all traffic so requests are routed to the controllers). Others recommended code solutions, but Sean Anderson’s solution on this stackoverflow.com post was the resolution for me.

There’s an extensionless URL hotfix (I blogged about this hotfix in relation to 404 errors) for IIS7 that is required: http://support.microsoft.com/kb/980368.

Razor ASP.NET web pages and CSHTML Forbidden errors

Recently, we had a support request come through for Cytanium’s ASP.NET 4.5 beta from a user trying to access an app written for ASP.NET web pages with Razor syntax. After publishing the files, the user was receiving the following YSOD:

Server Error in ‘/’ Application.


This type of page is not served.

Description: The type of page you have requested is not served because it has been explicitly forbidden.  The extension ‘.cshtml’ may be incorrect.   Please review the URL below and make sure that it is spelled correctly.
Requested URL: /testpage.cshtml

Normally, this is indicative of incorrect Application Pool settings. Razor syntax only works with ASP.NET 4.0 and requires the Integrated Pipeline to function properly. However, you also need to appropriate ASP.NET MVC files on the server – either in the GAC or deployed to your local /bin folder. Most people have ASP.NET MVC GAC’d on their development systems, so the application will work locally without having the appropriate DLL’s in the /bin folder of the web application. But that’s not necessarily the case on the server side. Per Microsoft’s recommendation, ASP.NET MVC is not GAC’d on the servers as there could be version issues that have a wide impact on all sites running on a shared host. Rather, it is recommended to bin deploy ASP.NET MVC DLL’s to each site. Once the appropriate DLL’s are in the /bin folder, and the app is running under ASP.NET 4.0 Integrate Pipeline, IIS will serve files written with Razor syntax.