Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Getting an ASP.NET Application Relative Path from a Logical Path


:P
On this page:

Every once in a while I run into the situation where I need an application root relative path in an ASP.NET application. Right now I need this for a few localization routines. In the ASP.NET Resource Provider model localization is based on resource sets which are referenced via – application relative paths (ie. subdir/page.aspx or page.aspx).

Question is how do you get this relative path?

For the current page this is actually quite easy. The HttpRequest object includes a handy AppRelativeCurrentExecutionFilePath function which returns virtual path syntax in an app relative basis:

/// <summary>
/// Translates the current ASP.NET path  
/// into an application relative path: subdir/page.aspx. The
/// path returned is based of the application base and 
/// starts either with a subdirectory or page name (ie. no ~)
/// 
/// This version uses the current ASP.NET path of the request
/// that is active and internally uses AppRelativeCurrentExecutionFilePath
/// </summary>
/// <returns></returns>
public static string GetAppRelativePath()
{
    return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.Replace("~/","");
}

The path returned starts off with the site based ~/ url specifier which can be easily stripped off to return just the root relative path.

This works great for the current ASP.NET request being processed but if you generically need to figure out a path there is no clean way to do this. So for the localization code I’m writing I need to potentially be able to retrieve a local resource for another page (mostly in an administrative capacity).

For this scenario I would like to be able to pass in a logical path (/virtual/subdir/page,aspx or /virtual/page.aspx) and have it converted to an app relative path. This is a bit more involved as there aren’t (as far as I can tell) any built in features to perform this conversion. However, it’s fairly straight forward to do by stripping out the ApplicationPath from the logical URL.

/// <summary>
/// Translates an ASP.NET path like /myapp/subdir/page.aspx 
/// into an application relative path: subdir/page.aspx. The
/// path returned is based of the application base and 
/// starts either with a subdirectory or page name (ie. no ~)
/// 
/// The path is turned into all lower case.
/// </summary>
/// <param name="logicalPath">A logical, server root relative path (ie. /myapp/subdir/page.aspx)</param>
/// <returns>Application relative path (ie. subdir/page.aspx)</returns>
public static string GetAppRelativePath(string logicalPath)
{            
    logicalPath = logicalPath.ToLower();
    string appPath = HttpContext.Current.Request.ApplicationPath.ToLower();
    if (appPath != "/")  
        appPath += "/";
    else
        // Root web relative path is empty
        return logicalPath.Substring(1);
    
    return logicalPath.Replace(appPath, "");            
}

The latter function is sort of like a Server.MapPath() but for mapping a logical path to a purely application relative path. Certainly this is not your  typical everyday function, but it comes up occasionally for generic tools and handlers that serve data that are specific to a given ASP.NET host request. Like a JavaScript resource handler that I’ve been working on recently.

Just so you don’t think I’m off my rocker HttpContext.GetLocalResourceObject() has an override that allows retrieving resources from another request even though this is probably an edge case – after all LOCAL resources are meant for the LOCAL request. However, in a handler that serves data – in this case JavaScript resources - for the current ‘parent’ page. In that scenario the local resource set is provided via query string so that the handler can then retrieve the local resources for the parent page rather than the current handler request which wouldn’t have any resources associated with it.

Just for demonstration here’s what one of the handler’s page registration (script tag embedding) methods look like. It basically picks up the current page’s relative page path and then creates a script link that embeds the appropriate resource set id into the URL (as well as the proper locale:

/// <summary>
/// Embed global JavaScript resources into the page.
/// 
/// This version returns resources of the active Resx or DB Resource Provider
/// and includes no controls 
/// </summary>
/// <param name="control">Page or Control instance required for JS registration</param> 
/// <param name="varName">Name of the JavaScript variable created</param>
/// <param name="localeId">The locale Id to retrieve data for</param>
public static void RegisterJavaScriptLocalResources(Control control, string varName, string localeId)
{
    ResourceProviderTypes type = ResourceProviderTypes.Resx;
    if (DbSimpleResourceProvider.ProviderLoaded || DbResourceProvider.ProviderLoaded)
        type = ResourceProviderTypes.DbResourceProvider;

    // get the current page path as a app relative path
    string resourceSet = WebUtils.GetAppRelativePath();

    RegisterJavaScriptLocalResources(control, varName, localeId, resourceSet, type, false);
}

 

It’s definitely not something that you’d use every day, but I’ve needed it on a few occasions and when I do I spend a while scratching my head thinking how the hell do I retrieve the relative path. So this time I created a generic library function and wrote this sucker up so I can hopefully remember.

Posted in ASP.NET  

The Voices of Reason


 

Ben Amada
March 28, 2009

# re: Getting an ASP.NET Application Relative Path from a Logical Path

Question: GetAppRelativePath() expects a parameter (logicalPath), but in your sample calling code, no parameter is passed into it ...

string resourceSet = WebUtils.GetAppRelativePath();

???

Rick Strahl
March 29, 2009

# re: Getting an ASP.NET Application Relative Path from a Logical Path

@Ben - There are two method signatures (see above). The one without the parameter uses the default ASP.NET path of the current request...

Dennis Gorelik
March 29, 2009

# re: Getting an ASP.NET Application Relative Path from a Logical Path

Rick,

Did you consider using VirtualPathUtility for that purpose?
http://msdn.microsoft.com/en-us/library/system.web.virtualpathutility.toabsolute.aspx

DotNetShoutout
March 29, 2009

# Getting an ASP.NET Application Relative Path from a Logical Path - Rick Strahl

Thank you for submitting this cool story - Trackback from DotNetShoutout

AC
March 29, 2009

# re: Getting an ASP.NET Application Relative Path from a Logical Path

I'd recommend against using HttpContext.Current to get these because you can't always get at HttpContext. I've seen this fail for a variety of reasons in library code that I've written and had to fix code recently. Instead, have a look in the System.Web.Hosting namespace for ways to get at some of this without needing to go via HttpContext.

    // using System.Web.Hosting.
    HostingEnvironment.ApplicationPhysicalPath;
    HostingEnvironment.ApplicationVirtualPath;
    HostingEnvironment.Cache;
    HostingEnvironment.MapPath();


Assuming also that the thread's culture is set accordingly, you can map paths as needed for localization.

Rick Strahl
March 30, 2009

# re: Getting an ASP.NET Application Relative Path from a Logical Path

@AC - the only time when HttpContext.Current should not be available is at design time. These methods wouldn't make sense at design time anyway, so it's not critical here.

But agreed HostingEnvironment is something that should be used when it makes sense - thanks for the reminder especially when design time needs to be taken into consideration.

AC
March 30, 2009

# re: Getting an ASP.NET Application Relative Path from a Logical Path

Rick we've found a few edge cases outside of a design time environment. I recently fixed a bug in a static constructor that occasionally exhibited the issue, and worker threads are susceptible to outliving the http context from which they originate.

http://www.odetocode.com/Articles/112.aspx by K. Scott Allen is a good reference to it.

I just wanted to point it out that unless you actually need the current http context for request or response objects, there are ways to get at some of the more useful things like the cache and mapping relative paths.

Rick Strahl
March 30, 2009

# re: Getting an ASP.NET Application Relative Path from a Logical Path

@AC - good point. Scott's post highlights threading as another point where HttpContext.Current doesn't work. HttpContext.Current only works on the current ASP.NET thread as the context is tied to it via TLS.

FWIW, the reason I usually don't go the route of HostingEnvironment is that it only provides a few base values that are typically not all that useful. They give you paths and conversions but obviously no current request. Anything that relies on an active request has no benefit of this.

Guitar Lessons
August 03, 2010

# re: Getting an ASP.NET Application Relative Path from a Logical Path

I am actually surprised that they didn't include probably the most useful way to get a domain as in: http://www.mydomain.com/appfolder

here is what I did:

Uri url = new Uri("https://www.whatever.com/wheremyappis/page37.aspx");
 
StringBuilder sb = new StringBuilder();
 
sb.Append(url.Scheme);
sb.Append("://");
sb.Append(url.Authority);
sb.Append(HttpContext.Current.Request.ApplicationPath);
sb.Append("/");
 
return sb.ToString();

Tzanimir Tzankov
August 18, 2010

# re: Getting an ASP.NET Application Relative Path from a Logical Path

Do you know a way to make this work with .NET 4.0 UrlRouting, where the path you are getting from Request object is not the path to the phisycal file like /test.aspx but some logical path /test/12332. In other words is there a way to convert in code the logical path to find the path of the currently executing aspx file. I need this, to be able to load Resources for custom control from the Page resource files.

Tzanimir

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024