52

I want to get the absolute root Url of an ASP.NET application dynamically. This needs to be the full root url to the application in the form: http(s)://hostname(:port)/

I have been using this static method:

public static string GetSiteRootUrl()
{
    string protocol;

    if (HttpContext.Current.Request.IsSecureConnection)
        protocol = "https";
    else
        protocol = "http";

    StringBuilder uri = new StringBuilder(protocol + "://");

    string hostname = HttpContext.Current.Request.Url.Host;

    uri.Append(hostname);

    int port = HttpContext.Current.Request.Url.Port;

    if (port != 80 && port != 443)
    {
        uri.Append(":");
        uri.Append(port.ToString());
    }

    return uri.ToString();
}

BUT, what if I don't have HttpContext.Current in scope? I have encountered this situation in a CacheItemRemovedCallback.

Fedor
  • 1,548
  • 3
  • 28
  • 38
saille
  • 9,014
  • 5
  • 45
  • 57
  • 2
    The simple answer to your question is that you can't... at least not with the scenario with the cache item removed callback. For example, one site can handle multiple URLs. Let's say you configure your app to handle requests for foo.com and bar.com . On the callback handler, you can't tell what the original request URL was. In your scenario, you'd have to store the request URL as part of your cached data, or as another user suggested, store it in your config. In other words, you need the HttpContext's request to determine the URL. – Makotosan Oct 29 '12 at 04:02

9 Answers9

87

For WebForms, this code will return the absolute path of the application root, regardless of how nested the application may be:

HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) + ResolveUrl("~/")

The first part of the above returns the scheme and domain name of the application (http://localhost) without a trailing slash. The ResolveUrl code returns a relative path to the application root (/MyApplicationRoot/). By combining them together, you get the absolute path of the web forms application.

Using MVC:

HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) + Url.Content("~/")

or, if you are trying to use it directly in a Razor view:

@HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority)@Url.Content("~/")
saluce
  • 13,035
  • 3
  • 50
  • 67
  • 2
    If you don't have an ASP.NET page available to call `ResolveUrl`, see [this answer](http://stackoverflow.com/questions/4893380/resolveurl-without-an-asp-net-page). – rockariotphobia Jan 16 '15 at 18:36
  • For MVC, you can pass `~/` to `Redirect()`, and it resolves it properly, so that's something at least. – jpaugh Jul 05 '17 at 19:56
  • 1
    The question specifically states HttpContext.Current is not available. – saille Jun 13 '18 at 04:26
27

You might try getting the raw URL and trimming off everything after the path forward slash. You could also incorporate ResolveUrl("~/").

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
12
public static string GetAppUrl()
{
    // This code is tested to work on all environments
    var oRequest = System.Web.HttpContext.Current.Request;
    return oRequest.Url.GetLeftPart(System.UriPartial.Authority)
        + System.Web.VirtualPathUtility.ToAbsolute("~/");

}
Denny Jacob
  • 375
  • 4
  • 9
7
public static string GetFullRootUrl()
{   
    HttpRequest request = HttpContext.Current.Request;
    return request.Url.AbsoluteUri.Replace(request.Url.AbsolutePath, String.Empty);
}
Brian Webster
  • 30,033
  • 48
  • 152
  • 225
Uri Gorobets
  • 71
  • 1
  • 5
2

I've solved this by adding a web.config setting in AppSettings ("SiteRootUrl"). Simple and effective, but yet another config setting to maintain.

saille
  • 9,014
  • 5
  • 45
  • 57
  • 2
    This doesn't really promote itself as a good method if you ever have to move the site. Moving is an infrequent event, to be sure...but you'll likely have forgotten this little "gem" and your site will be utterly broken at the new location. – saluce Dec 18 '12 at 15:39
  • 4
    Call me a contrarian, but I find this to be a simple, elegant solution that is highly portable and easy to maintain. I'd rather have a single setting to keep track of than be responsible for the reams of code suggested elsewhere in the answers to this question. – Daniel Szabo Jan 07 '14 at 23:55
  • 2
    I can say I have been using this technique for some time, on many applications with no ill-effects. When we move to a different environment we always have to visually scan through config files anyway & the change is both obvious and trivial. – saille Mar 15 '15 at 23:03
  • 2
    This was the only solution that worked for us. We needed to identify the root domain "example.com" if the url host is abc.example.com, but "test.example.com" if the url is test.example.com or abc.test.example.com. It's simply not possible to determine this from the Request url without hard-coding values. I tried hard to avoid requiring a config setting, but in the end it appears to be the simplest solution. – dhochee Jun 20 '15 at 02:05
1
 UrlHelper url = new UrlHelper(filterContext.RequestContext);
 string helpurl = url.Action("LogOn", "Account", new { area = "" },
                      url.RequestContext.HttpContext.Request.Url.Scheme);

Can get you the absolute url

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
predhin
  • 171
  • 3
1

@saluce had an excellent idea, but his code still requires an object reference and therefore can't run in some blocks of code. With the following, as long as you have a Current.Request the following will work:

With HttpContext.Current.Request
    Return .Url.GetLeftPart(UriPartial.Authority) + .ApplicationPath + If(.ApplicationPath = "/", Nothing, "/")
End With

This will work no matter the protocol, port, or root folder.

cjbarth
  • 4,189
  • 6
  • 43
  • 62
1

This has always worked for me

string root = Request.Url.AbsoluteUri.Replace(Request.Url.PathAndQuery, "");
erionpc
  • 368
  • 3
  • 15
-1

Based off Uri's but stripping query strings and handling when it is a virtual directory off IIS:

private static string GetSiteRoot()
{
    string siteRoot = null;
    if (HttpContext.Current != null)
    {
        var request = HttpContext.Current.Request;
        siteRoot = request.Url.AbsoluteUri
                .Replace(request.Url.AbsolutePath, String.Empty)        // trim the current page off
                .Replace(request.Url.Query, string.Empty);              // trim the query string off

        if (request.Url.Segments.Length == 4)
        {
            // If hosted in a virtual directory, restore that segment
            siteRoot += "/" + request.Url.Segments[1];
        }

        if (!siteRoot.EndsWith("/"))
        {
            siteRoot += "/";
        }
    }

    return siteRoot;
}
fiat
  • 15,501
  • 9
  • 81
  • 103
  • This doesn't handle virtual directories at all. It only works when the path is exactly a certain length. (The `Segments` property is merely a [concrete representation of path components](https://msdn.microsoft.com/en-us/library/system.uri.segments%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396) of the url, and has no idea about virtual directories, or any other abstraction beyond the URL itself.) – jpaugh Jul 05 '17 at 19:43
  • To clarify - it does handle 1-level deep virtual directories - but not n-level deep. And... this was for WebForms, not MVC. – fiat Jul 05 '17 at 22:57
  • 1
    It doesn't handle virtual directories at all. It only works if you have exactly 4 components in your path, and if the virtual directory consumes exactly the first component. The path to the virtual directory may consume only the first path component, or several; however, in either case, the full url can have more than 4 path components, irrespective of whether an app uses a virtual directory or not. – jpaugh Jul 05 '17 at 22:59
  • `Segments.Length >= 2` would be more generic; that would in fact work when the virtual directory always consumes exactly one segment. However, at that point it's probably better just to hardcode the virtual dir in the `web.config`, because it will only work in a few cases. – jpaugh Jul 05 '17 at 23:02