ASP.NET's Control class provides the handy ResolveUrl method that lets you easily use and parse relative URLs. ResolveUrl fixes up urls that start with ~ and puts the applications relative root path in its place. So if I'm in:

/wwstore/admin/Configuration.aspx

and I reference ~/images/someimage.gif ResolveUrl will return /wwstore/images/SomeImage.gif to me. This is much better than hardcoding /wwstore/images/someimage.gif or even using a relative path like ../images/someimage.gif. Using the ~ syntax makes the app much more resilient to pages or controls moving inside of the application. Most controls that have URL properties also use ResolveUrl so you can specify URLs on control properties using the same relative syntax.

Sometimes however, I find myself in some generic piece of Web code where I don't have access to the Page or a Control to actually use Control.ResolveUrl. For example, I was working on my Resource Provider recently and one of the options of the provider is to specify the resource administration for URL using - you guessed it ~ style relative path syntax. Deep inside the provider code I have no access to a page object - in fact the code may never actually hit a page handler at all but a custom HttpHandler instead.

So this issue comes up from time to time,  and it's useful to have a more generic ResolveUrl method. I thought that there was some method somewhere buried deep on HttpContext that could perform this fix up but I can't for the life of me find it. In the meantime I've been using the following code:

/// <summary>
/// Returns a site relative HTTP path from a partial path starting out with a ~.
/// Same syntax that ASP.Net internally supports but this method can be used
/// outside of the Page framework.
/// 
/// Works like Control.ResolveUrl including support for ~ syntax
/// but returns an absolute URL.
/// </summary>
/// <param name="originalUrl">Any Url including those starting with ~</param>
/// <returns>relative url</returns>
public static string ResolveUrl(string originalUrl)
{
    if (originalUrl == null)
        return null;
 
    // *** Absolute path - just return
    if (originalUrl.IndexOf("://") != -1)
        return originalUrl;
 
    // *** Fix up image path for ~ root app dir directory
    if (originalUrl.StartsWith("~"))
    {
        string newUrl = "";
        if (HttpContext.Current != null)
            newUrl = HttpContext.Current.Request.ApplicationPath +
                  originalUrl.Substring(1).Replace("//", "/");
        else
            // *** Not context: assume current directory is the base directory
            throw new ArgumentException("Invalid URL: Relative URL not allowed.");                                       
 
        // *** Just to be sure fix up any double slashes
        return newUrl;
    }
 
    return originalUrl;
}
 

You call it just like you would Control.ResolveUrl() and it will return an Application relative path.

Another related scenario is to resolve URLs into fully qualified absolute URLs. For example, in several applications I have callback URLs that get passed to various services like PayPal and Trackback services, that are supposed to call back your application. These pages need to get a fully qualified URL so they can find your page on the Web. Another scenario where I found this necessary is when you need to switch URLs into ssl as part of your application. For example, in my store you enter as a non-SSL URL until you hit the order pages at which point the page switches to SSL and you have to provide a fully qualified URL to do so.

/// <summary>
/// This method returns a fully qualified absolute server Url which includes
/// the protocol, server, port in addition to the server relative Url.
/// 
/// Works like Control.ResolveUrl including support for ~ syntax
/// but returns an absolute URL.
/// </summary>
/// <param name="ServerUrl">Any Url, either App relative or fully qualified</param>
/// <param name="forceHttps">if true forces the url to use https</param>
/// <returns></returns>
public static string ResolveServerUrl(string serverUrl, bool forceHttps)
{
    // *** Is it already an absolute Url?
    if (serverUrl.IndexOf("://") > -1)
        return serverUrl;
 
    // *** Start by fixing up the Url an Application relative Url
    string newUrl = ResolveUrl(serverUrl);
 
    Uri originalUri = HttpContext.Current.Request.Url;
    newUrl = (forceHttps ? "https" : originalUri.Scheme) + 
             "://" + originalUri.Authority + newUrl;
 
    return newUrl;
 
/// <summary>
/// This method returns a fully qualified absolute server Url which includes
/// the protocol, server, port in addition to the server relative Url.
/// 
/// It work like Page.ResolveUrl, but adds these to the beginning.
/// This method is useful for generating Urls for AJAX methods
/// </summary>
/// <param name="ServerUrl">Any Url, either App relative or fully qualified</param>
/// <returns></returns>
public static string ResolveServerUrl(string serverUrl)
{
    return ResolveServerUrl(serverUrl, false);
}

So the following applies:

// returns https://www.west-wind.com/wwStore/test.aspx
Response.Write( wwWebUtils.ResolveServerUrl("~/Test.aspx",true));

This is one of those things that's not needed frequently but when you do it's a pain to try and start remembering exactly how to retrieve these values. For me this happens often enough that I'll keep this in my toolbox.