Start a MVC3 web site HtmlHelper library and use NuGet to install it in new projects

Posted on April 28th, 2011

If your workload includes crafting multiple web sites you know that there are several components that are commonly used in each site. Some of those common elements are Google's Analytics tracking JavaScript and meta tags used in the site submission process to help verify site ownership to both Google and Bing. It would be super ideal if we could try and find a way to have that code already written and be able to quickly add it to a new web site project built upon ASP.NET MVC3. Lets make it happen.

Building a common MVC web site library

We will begin by creating a new Windows -> Class Library project named MvcWebsiteFramework.

New Project dialog

We can use this class library to add a whole bunch of commonly used code over time that will speed up our web site development. To start, we will look at how we can build a solution for working with the Google Analytics javascript and meta data verification tags.

MVC has a class, System.Web.Mvc.HtmlHelper, that has been around since the beginning to help render common HTML content. The core MVC framework is littered with extension methods that piggy back on this class to render things like form fields with Html.TextBox("MyField"). The Html in Html.TextBox is actually a property (that is of type System.Web.Mvc.HtmlHelper) of the System.Web.Mvc.ViewPage object available to each view. We can easily follow this pattern and create our own extension methods for the HtmlHelper object.

Before we write our extension methods, lets go over the markup that we will need to render.

The current Google Analytics tracking JavaScript looks like so:


The only piece of this JavaScript that changes from site to site is the UA number (UA-11111111-1 in our example code above). We will need a way to make that dynamic.

The meta tag for site verification with Google looks like:


The meta tag for site verification with Bing looks like:


Both of these will need to have a way to replace SOME_KEY with a unique value for each site.

Time to write some code. We will add a directory called HtmlHelpers to our project to keep our code organized and create two new class files in it, Bing.cs and Google.cs. We will put our extension methods that help us render our JavaScript and html into these classes.

The Bing.cs class:

using System.Web.Mvc;

namespace MvcWebsiteFramework.HtmlHelpers
{
    public static class Bing
    {
        public static MvcHtmlString BingSiteVerificationMetaTag(this HtmlHelper helper, string key)
        {
            var html = string.Format("", key);
            return MvcHtmlString.Create(html);
        }
    }
}

To create an extension method we need to make a static class and add a static method that takes in a reference to the object that the method will extend off of. Since we will be extending the System.Web.Mvc.HtmlHelper class we need to add that as our first method argument and use the keyword this. The second argument will be a string that will contain the key we get from Bing to verify our new site. Our method will return a System.Web.Mvc.MvcHtmlString object. The MVC engine expects this object to already be HTML-encoded and as a result, it won't apply any additional encoding. So when we use the Razor syntax to call the extension method it will not result in our html from our extension method getting encoded (and pieces like our greater than and less than tags will not get converted to > and <).

@Html.BingSiteVerificationMetaTag("THE_BING_KEY")

The name of our class isn't that important here as it is just acting as a container for our extension method. As you can see above, we will typically call this method off of an existing System.Web.Mvc.HtmlHelper object and not directly with Bing.BingSiteVerificationMetaTag. However, I have found it handy to use the name of this class to help keep my code organized and easy to read through when I have to come back to it 6 months from now because Bing or Google decides to do their verification or tracking code in a different way. One thing to note though, since we will be calling the extension methods off of the HtmlHelper object we cannot rely on our class or namespace structure to keep these methods unique. So we couldn't name the extension method SiteVerificationMetaTag in both the Bing and Google class because the compiler will not know which one to pick (the call would be ambiguous). Thus, we need to make each name unique and can do that by appending either Bing or Google to the method name.

The Google.cs class is going to have a method for the meta tag as well as a method for the tracking JavaScript:

using System;
using System.Text;
using System.Web.Mvc;

namespace MvcWebsiteFramework.HtmlHelpers
{
    public static class Google
    {
        public static MvcHtmlString GoogleSiteVerificationMetaTag(this HtmlHelper helper, string key)
        {
            var html = string.Format("", key);
            return MvcHtmlString.Create(html);
        }

        public static MvcHtmlString GoogleAnalyticsTrackingScript(this HtmlHelper helper, string accountId)
        {
            var html = new StringBuilder("");
            return MvcHtmlString.Create(html.ToString());
        }
    }
}

The GoogleSiteVerificationMetaTag looks just like the BingSiteVerificationMetaTag with the only difference being the meta tag name attribute value specific for Google. The GoogleAnalyticsTrackingScript gets a little bit nasty with the StringBuilder class, but there is really no clean approach to crafting this JavaScript. If we used the string.Format approach then we would have had to escape all of the curly brackets with a double curly. If we went with a multi-line string definition and closed/opened the string to inject the accountId then we would have had to start the re-open with another @. Multiple ways to skin a cat, but really no way to make that naked feline look any prettier.

With this framework in place we are ready to start utilizing it in new web site projects, which brings us to the next segment of our article:

Using NuGet to manage installation of our framework into new projects

If we build a NuGet package out of our framework library we can then use the to quickly install our framework into new projects. There is a lot that goes into creating and delivering a NuGet package (you can learn all of the details on the page of the NuGet CodePlex site), so lets see if we can simplify that a bit to get up and running fast. Here is an outline of what we need to accomplish:

  • Make sure you have the NuGet Package Manager installed in Visual Studio 2010.
  • the NuGet.exe application that will handle the build of our NuGet package.
  • Add a .nuspec manifest file that the NuGet.exe application will use.
  • Integrate the build of our package into our class library project.
  • Build our class library and verify that our NuGet package was created.
  • Set up the NuGet Package Manager to know where our package is at.
  • Verify that we can install the package with the NuGet Package Manager into a new web site project.

Assuming that you have the NuGet Package Manager installed and have downloaded the NuGet.exe, lets look at how we can create a .nuspec manifest file and integrate the execution of the NuGet.exe application into our build process. Begin by adding a new XML file named MvcWebsiteFramework.nuspec to the root of the MvcWebsiteFramework project. The content of the file:



    
        MvcWebsiteFramework
        1.0.0
        Justin Schwartzenberger
        
            MvcWebsiteFramework is a class library of common MVC3 web site code that can be used as a 
            foundation for a new web site project.
        
    
    
        
    

There is a great post by Vadim Kreynin called Baby Steps to Create a NuGet Package that describes how the fields in the manifest file are used in the NuGet Package Manager application.

With the manifest file in place we can turn our attention to the build process for our project. We want to have our build process automatically run the NuGet.exe application to build our package. For this to happen, we will need to include the NuGet.exe in our solution. Add a New Solution Folder named Tools to the solution, navigate to the solution folder in Windows Explorer and create a new directory named Tools, put your downloaded NuGet.exe file into that directory, and then back in Visual Studio right click on the Solution -> Tools directory and Add -> Existing Item to add the Tools/NuGet.exe file. Our solution tree should now look like so:

Solution Tree

If we double click on the MvcWebsiteFramework project Properties and click the Build Events tab we can edit the Post-build event command line value to add a command to call the NuGet.exe application:

"$(SolutionDir)Tools\NuGet.exe" pack "$(ProjectDir)MvcWebsiteFramework.nuspec"

This will execute NuGet.exe from within our solution Tools directory, call the pack option, and reference the manifest file located in our project directory. A new file called MvcWebsiteFramework.1.0.0.nupkg will get created in our build output directory.

NOTE
I downloaded the NuGet.exe from the and put it into the Tools dir, and upon building my project with the above build event for the first time the call to NuGet.exe resulted in the application self-updating its version, and thus the NuGet package wasn't created. A second build resulted in the package build since my NuGet.exe was up to date at that point. So you can either do what I did and be aware of that gotcha, or you could open a command line to the Tools dir and run NuGet.exe with no arguments to have it self-update prior to running your project build for the first time with the build event in place.

Next we need to update the settings for the NuGet Package Manager in Visual Studio to make it aware of our package location. In Visual Studio, go to Tools -> Options to bring up the Options dialog. In the tree on the left, expand the Package Manager node and click on Package Sources. In the Name field we can put "MvcWebsiteFramework". Click on the ellipses next to the Source field to browse for the folder that contains our MvcWebsiteFramework.1.0.0.nupkg file (located in the /bin/Debug dir in our MvcWebsiteFramework project dir). When finished, click the "Add" button to add the package source.

Package Source Add

We should now see our package location in the list:

Package Added

NOTE
I added the path to the bin/Debug simply to illustrate how to add a package source without having to show and explain a bunch of other steps. When you are ready to actually use this process it will be ideal to either have a deploy process upon build to put your NuGet package in some sort of shared file dir or even set up a stand alone NuGet package feed site and have your package deployed as a part of that.

Finally, we are ready to create a new MVC3 Web site and use the NuGet Package Manager to add our framework library. Create a new MVC 3 project, select the Internet Application template, and target the Razor view engine. This will give us a project with some controllers and views already set up so we can test after we install our package. From here we can right click on the project name and select Add Library Package Reference... to bring up the NuGet Package Manager dialog. In the nav tree on the left we can see our MvcWebsiteFramework package source listed below the official NuGet source. If we select that we can see the package we created along with the details from our manifest file.

Package Installer

Click the Install button and NuGet will work its magic, adding our dll and adding a reference to it in our project. We are all set to start using our code. Open up the Views/Shared/_Layout.cshtml file and add a @using statement to reference our namespace for our classes that contain our extension methods and then add the calls to our HtmlHelper extension methods:

@using MvcWebsiteFramework.HtmlHelpers



    
    @ViewBag.Title
    
    
    
    @Html.GoogleSiteVerificationMetaTag("THE_GOOGLE_KEY")
    @Html.BingSiteVerificationMetaTag("THE_BING_KEY")
    @Html.GoogleAnalyticsTrackingScript("UA-11111111-1")


    

My MVC Application

@Html.Partial("_LogOnPartial")
@RenderBody()

Do an F5 and view the source on the page and we will see our meta tags and JavaScript rendered out.

    
    
    

And that's a wrap. We have created the foundation for a MVC3 web site library and set up a pipeline for easy deployment to new web site projects.

Where do we go from here?

  • We could update our NuGet package to add a web.config transform that will add our namespace to the node in the web.config so we don't have to put @using statements in our views. You can learn more about that process .
  • We can add a build step to our framework project that deploys the NuGet package to a shared directory or create our own NuGet Package Feed site by reading Phil Haack's post and then update our NuGet Package Manager options in Visual Studio to point to one of those instead of our framework project bin/Debug directory (this is highly recommended!).
  • We could expand upon our MvcWebsiteFramework library to add other common functionality or even include Controller class code like the Search Engine Crawler Access code from one of my previous posts.
  • We could add a Unit Test project to our framework solution and learn how to do unit testing on HtmlHelper extension methods by using mock HttpContext and ViewDataContainer objects. Check out Craig Stuntz’s great post on .

Updates to prevent Cross Site Scripting injection

To use the TagBuilder class you first need to add a reference to System.Web.WebPages to the MvcWebsiteFramework class library project as the TagBuilder class now resides in that namespace in MVC3. Once that is added we can begin with the updates to our code.

Updates to the Bing HtmlHelper:

using System.Web.Mvc;

namespace MvcWebsiteFramework.HtmlHelpers
{
    public static class Bing
    {
        public static MvcHtmlString BingSiteVerificationMetaTag(this HtmlHelper helper, string key)
        {
            var html = new TagBuilder("meta");
            html.MergeAttribute("name", "msvalidate.01");
            html.MergeAttribute("content", key);
            return MvcHtmlString.Create(html.ToString(TagRenderMode.SelfClosing));
        }
    }
}

The update for the Google HtmlHelper is the same for the meta tag, but the analytics tracking code is a bit different. The analytics code is a script tag that can be created with the TagBuilder, but we need to put the contents of the tab in using the TagBuilder.InnerHtml property. Since the TagBuilder.InnerHtml will not do anything to protect us from XSS we need to handle the accountId prior to building that content. What we can do is make a call to the HttpUtility.HtmlEncode class to update our accountId variable first. If some malicious value was sent in via the GoogleAnalyticsTrackingScript method call this would handle that. Then we can start our TagBuilder instance for a