views:

175

answers:

6

I am really surprised that there is no native .NET method to get an absolute url from a relative url. I know this has been discussed many times, but never have come across a satisfactory method that handles this well. Can you help fine tune the method below?

I think all I need left is to auto choose the protocol instead of hard coding it (http/https). Anything else I am missing (caveats, performance, etc)?

public static string GetAbsoluteUrl(string url)
    {
        //VALIDATE INPUT FOR ALREADY ABSOLUTE URL
        if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
        { 
            return url;
        }

        //GET PAGE REFERENCE FOR CONTEXT PROCESSING
        Page page = HttpContext.Current.Handler as Page;

        //RESOLVE PATH FOR APPLICATION BEFORE PROCESSING
        if (url.StartsWith("~/"))
        {
            url = page.ResolveUrl(url);
        }

        //BUILD AND RETURN ABSOLUTE URL
        return "http://" + page.Request.ServerVariables["SERVER_NAME"] + "/" + url.TrimStart('/');
    }
+1  A: 

check the following code to retrieve absolute Url :

Page.Request.Url.AbsoluteUri

I hope to be useful.

DEVMBM
A: 

With ASP.NET, you need to consider the reference point for a "relative URL" - is it relative to the page request, a user control, or if it is "relative" simply by virtue of using "~/"?

The Uri class contains a simple way to convert a relative URL to an absolute URL (given an absolute URL as the reference point for the relative URL):

var uri = new Uri(absoluteUrl, relativeUrl);

If relativeUrl is in fact an abolute URL, then the absoluteUrl is ignored.

The only question then remains what the reference point is, and whether "~/" URLs are allowed (the Uri constructor does not translate these).

Stephen Cleary
A: 

Try this:

Uri uri = new Uri("some relative url", UriKind.Relative);
string absPath = uri.AbsolutePath;
A: 

Still nothing good enough using native stuff. Here is what I ended up with:

public static string GetAbsoluteUrl(string url)
{
    //VALIDATE INPUT
    if (String.IsNullOrEmpty(url))
    {
        return String.Empty;
    }

    //VALIDATE INPUT FOR ALREADY ABSOLUTE URL
    if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
    { 
        return url;
    }

    //GET CONTEXT OF CURRENT USER
    HttpContext context = HttpContext.Current;

    //RESOLVE PATH FOR APPLICATION BEFORE PROCESSING
    if (url.StartsWith("~/"))
    {
        url = (context.Handler as Page).ResolveUrl(url);
    }

    //BUILD AND RETURN ABSOLUTE URL
    string port = (context.Request.Url.Port != 80 && context.Request.Url.Port != 443) ? ":" + context.Request.Url.Port : String.Empty;
    return context.Request.Url.Scheme + Uri.SchemeDelimiter + context.Request.Url.Host + port + "/" + url.TrimStart('/');
}
TruMan1
A: 

One little suggestion for your code. You should use String.Format instead of the concatenating the URL bits in the end.

Bernd
+1  A: 

This has always been my approach to this little nuisance. Note the use of VirtualPathUtility.ToAbsolute(relativeUrl) allows the method to be declared as an extension in a static class.

/// <summary>
/// Converts the provided app-relative path into an absolute Url containing the full host name
/// </summary>
/// <param name="relativeUrl">App-Relative path</param>
/// <returns>Provided relativeUrl parameter as fully qualified Url</returns>
/// <example>~/path/to/foo to http://www.web.com/path/to/foo&lt;/example&gt;
public static string ToAbsoluteUrl(this string relativeUrl) {
    if (string.IsNullOrEmpty(relativeUrl))
        return relativeUrl;

    if (HttpContext.Current == null)
        return relativeUrl;

    if (relativeUrl.StartsWith("/"))
        relativeUrl = relativeUrl.Insert(0, "~");
    if (!relativeUrl.StartsWith("~/"))
        relativeUrl = relativeUrl.Insert(0, "~/");

    var url = HttpContext.Current.Request.Url;
    var port = url.Port != 80 ? (":" + url.Port) : String.Empty;

    return String.Format("{0}://{1}{2}{3}",
           url.Scheme, url.Host, port, VirtualPathUtility.ToAbsolute(relativeUrl));
}
Nathan Taylor
Quick note.. sometimes I do not want the root of the path, but a root to where I am. Something like this: Page.ResolveUrl("SomePage.aspx"). In your method, this will assume I want ~/SomePage.aspx when I really want ~/path/to/where/I/currently/am/SomePage.aspx. Great method!
TruMan1
..by the way VirtualPathUtility.ToAbsolute requires you to have a ~ in the beginning. But if I want a relative path to where I am, then (context.Handler as Page).ResolveUrl works.
TruMan1
Hummm how did I manage to make this community wiki? >.< @TruMan1 It would be semantically impossible in an external extension method (like this one) to derive a location from "Something.aspx", which is why the method automatically appends a "~/" to the front of the path. That being said, your (context.Handler as Page) scenario works so long as the Handler is always a Page. That solution would certainly fail if invoked from an HttpHandler or similar.
Nathan Taylor
Good catch. I would still like to use ResolveUrl as a priority for the scenario mentioned... so I added a condition to check if page is null, otherwise use the the VirtualPathUtility.
TruMan1