How to get base URL in ASP.NET Core
You typically don't and shouldn't need to know where a web app is deployed. At least not from within the code of the web app itself. I keep seeing questions related to this, though. There are a range of reasons why this can still be relevant like if you want to generate and output an absolute URL in an MVC controller or Razor page. Here's a blog post about how to get the base URL in ASP.NET Core.
Let's rewind a bit before we start looking into the code. All websites are deployed somewhere. This can be locally on your developer machine, test environment, production environment running on Azure, or something else. Common for all websites is that you will need a URL to access different pages on the website. Example:
https://example.com/path/document.js?id=42
The URL consists of a range of parts. https
is the schema, example.com
is the hostname, path/document.js
is the path, and id=42
is the query string. When talking about the base URL that is the theme of discussion here, that part is a combination of the scheme, port, and hostname:
https://example.com
As you probably already know, the port number can be omitted as long as the web server listens on port 443
for https
or port 80
for http
requests.
With that out of the way, let's get to the point. So, how do we get access to this information in ASP.NET Core? To my knowledge, there are no built-in ways to get the base URL, but the request provides enough information to put it together manually. In MVC it's easy:
public class HomeController : Controller
{
public IActionResult Index()
{
var baseUri = $"{Request.Scheme}://{Request.Host}:{Request.Host.Port ?? 80}";
return View();
}
}
This example is what typically is proposed when people ask on Stack Overflow or similar. There are some downsides to using string interpolation like this. The obvious one is the port part which we in most cases don't want to be included. Luckily, .NET comes with a built-in class for creating URLs named UriBuilder
. Let's refactor the code a bit:
var uriBuilder = new UriBuilder(Request.Scheme, Request.Host.Host, Request.Host.Port ?? -1);
if (uriBuilder.Uri.IsDefaultPort)
{
uriBuilder.Port = -1;
}
var baseUri = uriBuilder.Uri.AbsoluteUri;
In this example, we use the UriBuilder
class to add the scheme and hostname and only add the port if it's in the request. The value of -1
(strangely) represents no port in UriBuilder
. The if
-statement included is to avoid showing the port number in the base URL if it's the default for the scheme. So, in case scheme
is https
and port
is 443
we just want https://example.com
and not https://example.com:443
.
To make this even simpler to use, we can include this code as an extension method on HttpRequest
by creating a new class named HttpRequestExtensions
:
public static class HttpRequestExtensions
{
public static string? BaseUrl(this HttpRequest req)
{
if (req == null) return null;
var uriBuilder = new UriBuilder(req.Scheme, req.Host.Host, req.Host.Port ?? -1);
if (uriBuilder.Uri.IsDefaultPort)
{
uriBuilder.Port = -1;
}
return uriBuilder.Uri.AbsoluteUri;
}
}
Getting hold of the base URL only requires a single line now.
In Razor Pages, you won't have access to the HTTP request like in MVC. To make this work, start by including the HttpContextAccessor
in the Program.cs
file (Startup.cs
if you are on earlier versions of ASP.NET Core):
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpContextAccessor();
// ...
var app = builder.Build();
// ...
}
Once that is included you can inject the IHttpContextAccessor
in the Razor page:
{
private readonly IHttpContextAccessor httpContextAccessor;
public IndexModel(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public void OnGet()
{
var baseUrl = httpContextAccessor.HttpContext?.Request.BaseUrl();
// ...
}
}