MVC Html.Resolve() – Resolve URLs to your page

For the current MVC application I’m developing, I needed to host the site under a IIS Virtual Directory.  This caused a problem as I can’t use the following code:

<%=Html.Image("~/Content/icon_green.gif") %>

This would ignore the fact that I was under a Virtual Directory and point to the root site – http://mysite/ instead of http://mysite/myvirtual/ like I want.

Rob Conery mentioned a Html.ResolveUrl method, however I couldn’t find the method on the HtmlHelper so I wrote my own.  The method is called Resolve and is an extension to the HtmlHelper within MVC. Here is one of the unit tests for it.

[Fact]
public void Resolve_With_IIS_Virtual_Directory_Returns_Full_Path()
{
    HtmlHelper html = CreateHtmlHelper(“
http://localhost/TestProject”);

    string result = html.Resolve(“~/Content/t.jpg”);
    Assert.Equal(“
http://localhost/TestProject/Content/t.jpg”, result);
}

Implementation is very simple, we prefix the virtual directory with the application path if required.

public static string Resolve(this HtmlHelper helper, string virtualUrl)
{
    if (!virtualUrl.StartsWith(“~/”))
        return virtualUrl;

    virtualUrl = virtualUrl.Remove(0, 2);

    string applicationPath = helper.ViewContext.HttpContext.Request.ApplicationPath;
    if(string.IsNullOrEmpty(applicationPath) || !applicationPath.EndsWith(“/”))
    {
        applicationPath = applicationPath  + “/”;
    }

    return applicationPath + virtualUrl;
}

However, this brought up an interesting point. How do you test extension methods? It turns out, you can easily test the methods, you just need to create the object they are extending and call the method – obviously.

The problem with this is, HtmlHelper (the object I’m extending) has a number of dependencies, for example in my Resolve method I’m calling HttpContext in order to get the ApplicationPath.

In order to create the HtmlHelper object, I added myself a little helper method. I’m using Rhino Mocks 3.5 in order to generate the stubs, sadly ViewContext requires a number of different parameters (the only one I care about is httpContext), as a result I had to generate a number of different stubs.

private HtmlHelper CreateHtmlHelper(string appPath)
{
    HttpContextBase httpContext = MockRepository.GenerateStub();
    HttpRequestBase httpRequestBase = MockRepository.GenerateStub();
    httpRequestBase.Stub(h => h.ApplicationPath).Return(appPath);
    httpContext.Stub(h => h.Request).Return(httpRequestBase);

    ViewContext viewContext = MockRepository.GenerateStub(httpContext,
                                                                    new RouteData(),
                                                                    MockRepository.GenerateStub(),
                                                                    “Test”, “MasterTest”, new ViewDataDictionary(),
                                                                    new TempDataDictionary(httpContext));

    return new HtmlHelper(viewContext, MockRepository.GenerateStub());
}

The end result is that I have my HtmlHelper object created which returns a known ApplicationPath, this allows me to fully test my extension method. However, I have had put in some additional effort due to requiring ViewContext.

Download Tests: HtmlExtensionTests.txt
Download Implementation: Html.txt

Technorati Tags:

3 thoughts on “MVC Html.Resolve() – Resolve URLs to your page”

  1. It’s cool that you managed to implement a testable extension method like that.

    Just for completeness, you can also use:

    < %= Html.Content("~/my/thing.ext") %>

    or just

    < %= ResolveUrl("~/my/thing.ext") %>

    They both do the same thing.

Leave a Reply

Your email address will not be published. Required fields are marked *