2.14.2010

Tips on using Virtual Directories in ASP.NET, MVC, HttpSimulator, SiteMap

Imagine you develop a web-site. You register IIS site to test it, seems like all is ok. You deploy it to customer, and... oh no! where is my images, styles, scripts? and why site map causes an exception?!
It is common situation when site developed and tested on IIS web-site root, and deployed into Virtual Directory. Here I want to describe some tips to avoid such situations.

1. Do not use relative paths.
It is a first and biggest pain. Convert relative paths to absolute using:
VirtualPathUtility.ToAbsolute(virtualPath);
2. Add trailing slash to default path.
When you use MVC, you can have an exception in your site map (I use MvcSiteMap provider) when you request your site without trailing slash. For example, this request may works fine:
http://siteroot/testvd/
but this one cause an exception when you try to access SiteMap.CurrentNode:
http://siteroot/testvd
Why? Hmm... it is an interesting question, I saw such situations on IIS 5,6 configured to work with MVC. If not to go very deep in ASP.NET - because registered path in site map contains trailing slash, and there is no path without slash. It is standard behavior of ASP.NET. In WebForms (not MVC) site, when you try to access such path, IIS redirects you to path with trailing slash automatically. I think, since here IIS send all requests directly to ASP.NET (because of registered wildcard or .* extension used for MVC works fine), it can't redirect user to page with trailing slash.
To fix this you can add next code in your Global.asax.cs:
/// <summary>
/// Starts each request
/// </summary>
protected void Application_BeginRequest()
{
        if (Request.ApplicationPath == Request.Path)
                Context.RewritePath(VirtualPathUtility.AppendTrailingSlash(Request.ApplicationPath), true);
}
3. Testing site in VD using HttpSimulator, developed by Phil Haack, can cause unexpected result.
Here is pseudo-code that can cause incorrect results:
string path = string.Empty;
using (new HttpSimulator("/TestVD").SimulateRequest())
{
        path = VirtualPathUtility.ToAbsolute("/images/Test.png");
}

// expected result: path = "/TestVD/images/Test.png";
// actual result: path = "/TestVDimages/Test.png";
To correct this you must change code of method SetHttpRuntimeInternals in HttpSimulator.cs (changed lines are highlighted):
void SetHttpRuntimeInternals()
{
        // ...
        // set app virtual path property value
        string vpathTypeName = "System.Web.VirtualPath, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
        string appPath = VirtualPathUtility.AppendTrailingSlash(ApplicationPath);
        object virtualPath = ReflectionHelper.Instantiate(vpathTypeName, new Type[] { typeof(string) }, new object[] { appPath });
        ReflectionHelper.SetPrivateInstanceFieldValue("_appDomainAppVPath", runtime, virtualPath);
        // ...
}

Shout it

kick it on DotNetKicks.com

3 comments:

  1. Great!! Excactly what i was looking for :)

    ReplyDelete
    Replies
    1. Cool to hear that it helps to someone else, I'll try to keep posting such a things.

      Delete
  2. Is it possible to do the same for WebAPI application?

    I have a web api application, in which I have few apis and also have an angular app which consumes those apis.

    everything works fine on local but once I release it on another environment(with virtual path), url rewriting logic breaks.

    ReplyDelete