58

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('/');
    }
SteveC
  • 13,636
  • 21
  • 86
  • 155
TruMan1
  • 27,054
  • 46
  • 150
  • 273
  • 1
    One little suggestion for your code. You should use String.Format instead of the concatenating the URL bits in the end. – Bernd Sep 10 '10 at 05:22

11 Answers11

92

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</example>
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));
}
SteveC
  • 13,636
  • 21
  • 86
  • 155
Nathan Taylor
  • 23,720
  • 17
  • 90
  • 152
  • 2
    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 Sep 23 '10 at 21:29
  • ..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 Sep 23 '10 at 21:41
  • 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 Sep 23 '10 at 22:23
  • 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 Sep 24 '10 at 00:12
  • 3
    Great method, just a small modification to not add port when its on https "var port = url.Port == 80 || (url.Scheme == "https" && url.Port == 443) ? "" : ":" + url.Port;" – Paleta Feb 14 '14 at 19:02
  • You really need to modify the sample to add the code in @Paleta's comment – JJS Mar 18 '15 at 20:43
  • 3
    Old post, but I think it would be better to use `url.IsDefaultPort` rather than checking port numbers for both http and https instances. – goelze Aug 13 '16 at 17:35
  • That was so easy... Thank You – Thameem Sep 20 '19 at 06:51
69
new System.Uri(Page.Request.Url, "/myRelativeUrl.aspx").AbsoluteUri
g.breeze
  • 1,773
  • 17
  • 22
  • 1
    Thank you. This works very well at least for a Windows Store app. – Hong Jan 17 '14 at 04:22
  • think this should be the answer now.. works excellent for web application as well – Nirman Nov 30 '15 at 07:02
  • This seems to fail if the relative Url provided is actually an absolute URL. For instance, `new System.Uri("http://example.com", "http://google.com")` should return the google link. – Nacht Jan 17 '17 at 06:22
  • 1
    I disagree. This works also when the relative url is absolute. It does however not work if you have "~" in your relative url. – wezzix Aug 14 '18 at 06:19
14

This one works for me...

new System.Uri(Page.Request.Url, ResolveClientUrl("~/mypage.aspx")).AbsoluteUri
Carter Medlin
  • 10,482
  • 4
  • 55
  • 65
5

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
  • 376,315
  • 69
  • 600
  • 728
3

Here is my own version that handles many validations and relative pathing from user's current location option. Feel free to refactor from here :)

/// <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</example>
public static string GetAbsoluteUrl(string relativeUrl)
{
    //VALIDATE INPUT
    if (String.IsNullOrEmpty(relativeUrl))
        return String.Empty;
    //VALIDATE INPUT FOR ALREADY ABSOLUTE URL
    if (relativeUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase) 
    || relativeUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
        return relativeUrl;
    //VALIDATE CONTEXT
    if (HttpContext.Current == null)
        return relativeUrl;
    //GET CONTEXT OF CURRENT USER
    HttpContext context = HttpContext.Current;
    //FIX ROOT PATH TO APP ROOT PATH
    if (relativeUrl.StartsWith("/"))
        relativeUrl = relativeUrl.Insert(0, "~");
    //GET RELATIVE PATH
    Page page = context.Handler as Page;
    if (page != null)
    {
        //USE PAGE IN CASE RELATIVE TO USER'S CURRENT LOCATION IS NEEDED
        relativeUrl = page.ResolveUrl(relativeUrl);
    }
    else //OTHERWISE ASSUME WE WANT ROOT PATH
   {
        //PREPARE TO USE IN VIRTUAL PATH UTILITY
        if (!relativeUrl.StartsWith("~/"))
            relativeUrl = relativeUrl.Insert(0, "~/");
        relativeUrl = VirtualPathUtility.ToAbsolute(relativeUrl);
    }

    var url = context.Request.Url;
    var port = url.Port != 80 ? (":" + url.Port) : String.Empty;
    //BUILD AND RETURN ABSOLUTE URL
    return String.Format("{0}://{1}{2}{3}",
           url.Scheme, url.Host, port, relativeUrl);
}
SteveC
  • 13,636
  • 21
  • 86
  • 155
Basem
  • 574
  • 1
  • 8
  • 25
1

If you're in the context of an MVC Controller or View you can use the UrlHelper which should be accessible via just Url

Url.Content("~/content/images/myimage.jpg")

Which will be fully expanded to /virtual_directoryname/content/images/myimage.jpg

This can be used in a controller or .cshtml file

Yes it is a little odd that it's called Content but it's meant to be used to get an absolute path to a resource so it makes sense

Simon_Weaver
  • 120,240
  • 73
  • 577
  • 618
1

When you want to generate URL from your Business Logic layer, you do not have the flexibility of using ASP.NET Web Form's Page class/ Control's ResolveUrl(..) etc. Moreover, you may need to generate URL from ASP.NET MVC controller too where you not only miss the Web Form's ResolveUrl(..) method, but also you cannot get the Url.Action(..) even though Url.Action takes only Controller name and Action name, not the relative url.

I tried using

var uri = new Uri(absoluteUrl, relativeUrl)

approach, but there is a problem too. If the web application is hosted in IIS virtual directory, where the url of the app is like this : http://localhost/MyWebApplication1/, and the relative url is "/myPage" then the relative url is resolved as "http://localhost/MyPage" which is another problem.

Therefore, in order to overcome such problems, I have written a UrlUtils class which can work from a class library. So, it wont depend on Page class but it depends on ASP.NET MVC. So, if you dont mind adding reference to MVC dll to your class library project then my class will work smoothly. I have tested in IIS virtual directory scenario where the web application url is like this : http://localhost/MyWebApplication/MyPage. I realized that, sometimes we need to make sure that the Absolute url is SSL url or non SSL url. So, I wrote my class library supporting this option. I have restricted this class library so that the relative url can be absolute url or a relative url that starts with '~/'.

Using this library, I can call

string absoluteUrl = UrlUtils.MapUrl("~/Contact");

Returns : http://localhost/Contact when the page url is : http://localhost/Home/About

Returns : http://localhost/MyWebApplication/Contact when the page url is : http://localhost/MyWebApplication/Home/About

  string absoluteUrl = UrlUtils.MapUrl("~/Contact", UrlUtils.UrlMapOptions.AlwaysSSL);

Returns : **https**://localhost/MyWebApplication/Contact when the page url is : http://localhost/MyWebApplication/Home/About

Here is my class Library :

 public class UrlUtils
    {
        public enum UrlMapOptions
        {
            AlwaysNonSSL,
            AlwaysSSL,
            BasedOnCurrentScheme
        }

        public static string MapUrl(string relativeUrl, UrlMapOptions option = UrlMapOptions.BasedOnCurrentScheme)
        {
            if (relativeUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
                relativeUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
                return relativeUrl;

            if (!relativeUrl.StartsWith("~/"))
                throw new Exception("The relative url must start with ~/");

            UrlHelper theHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);

            string theAbsoluteUrl = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) +
                                           theHelper.Content(relativeUrl);

            switch (option)
            {
                case UrlMapOptions.AlwaysNonSSL:
                    {
                        return theAbsoluteUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
                            ? string.Format("http://{0}", theAbsoluteUrl.Remove(0, 8))
                            : theAbsoluteUrl;
                    }
                case UrlMapOptions.AlwaysSSL:
                    {
                        return theAbsoluteUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
                            ? theAbsoluteUrl
                            : string.Format("https://{0}", theAbsoluteUrl.Remove(0, 7));
                    }
            }

            return theAbsoluteUrl;
        }
    }   
Emran Hussain
  • 9,974
  • 5
  • 36
  • 44
1

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
  • 27,054
  • 46
  • 150
  • 273
1

The final version taking care of all previous complaints (ports, logical url, relative url, existing absolute url...etc.) considering the current handler is the page:

public static string ConvertToAbsoluteUrl(string url)
{
    if (!IsAbsoluteUrl(url))
    {
        if (HttpContext.Current != null && HttpContext.Current.Request != null && HttpContext.Current.Handler is System.Web.UI.Page)
        {
            var originalUrl = HttpContext.Current.Request.Url;
            return string.Format("{0}://{1}{2}{3}", originalUrl.Scheme, originalUrl.Host, !originalUrl.IsDefaultPort ? (":" + originalUrl.Port) : string.Empty, ((System.Web.UI.Page)HttpContext.Current.Handler).ResolveUrl(url));
        }
        throw new Exception("Invalid context!");
    }
    else
        return url;
}

private static bool IsAbsoluteUrl(string url)
{
    Uri result;
    return Uri.TryCreate(url, UriKind.Absolute, out result);
}
yazanpro
  • 3,752
  • 4
  • 39
  • 59
0

check the following code to retrieve absolute Url :

Page.Request.Url.AbsoluteUri

I hope to be useful.

DEVMBM
  • 492
  • 1
  • 5
  • 17
-1

This works fine too:

HttpContext.Current.Server.MapPath(relativePath)

Where relative path is something like "~/foo/file.jpg"

abatishchev
  • 92,232
  • 78
  • 284
  • 421
Txaran
  • 107
  • 1
  • 11
  • 1
    I have to disagree. Server.MapPath refers to a physical location, not absolute Uri like the OP request. Reference http://stackoverflow.com/questions/275781/server-mappath-server-mappath-server-mappath-server-mappath – Hoàng Long Dec 29 '15 at 02:48