117

Say my website is hosted in the mywebsite folder of www.example.com and I visit https://www.example.com/mywebsite/home/about.

How do I get the base url part in an MVC controller? The part that I am looking for is https://www.example.com/mywebsite

The example listed here doesn't work as we don't have access to Request.Url in ASP.NET Core

jao
  • 18,273
  • 15
  • 63
  • 96
  • @VSO if you look at the answer linked by the OP you will see an updated version for core that you should be able to convert into a service so that you can access the request anywhere in the app during the scope of a request. https://stackoverflow.com/a/12144292/5233410 The provided answer in this question is correct. What do you not understand? – Nkosi Oct 30 '17 at 21:59

7 Answers7

182

You should still be able to piece together what you need. You have access to the request object if your controller inherits from Controller.

If you are using VS2017, fire up a new ASPNet Core MVC app and replace the homecontroller with:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public IActionResult About()
    {
        ViewData["Message"] = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";

        return View();
    }

    public IActionResult Contact()
    {
        ViewData["Message"] = "Your contact page.";

        return View();
    }

    public IActionResult Error()
    {
        return View();
    }
}

I just put in some of the stuff that might interest you in the "About" method, but you should explore the rest of the request class so you know what else is available.

As @Tseng pointed out, you might have a problem when running Kestrel behind IIS or Azure App Service, but if you use the IISIntegration package or AzureAppServices package (by installing the Nuget package and adding it in Program.cs to your WebHostBuilder), it should forward those headers to you. It works great for me in Azure, because I sometimes have to make decisions based on which hostname they hit. The IIS/Azure packages also forward the original remote IP address, which I log.

NPNelson
  • 2,940
  • 2
  • 16
  • 27
  • 4
    That should be Request.Scheme + "://" + Request.Host + Request.PathBase – Tratcher Apr 21 '17 at 03:07
  • 5
    I updated my answer to use **PathBase** instead of **Path**. Thanks to Tratcher for pointing that out. – NPNelson Apr 21 '17 at 10:55
  • 1
    I'd like to point out how terrible of an idea this is. A user can change "Url.Request". Don't use this anywhere near user information. Imagine a well-crafted "Forgot Password" request. – johnrom Jan 28 '19 at 22:44
  • Works if I append .Value.ToString() $"{this.Request.Scheme}://{this.Request.Host.Value.ToString()}{this.Request.PathBase.Value.ToString()}"; – hubert17 May 14 '19 at 07:02
  • On consideration, I think it is actually quite easy for this value to get stored and used later without knowing it originally came from (potentially malicious) user input. I implemented a whitelist to mitigate this. Alternatively, clients that use this baseurl could also issue a challenge to the server of some kind. – jmathew Jun 01 '20 at 20:56
  • CustomPort is not considered in this expression, right? – Daniel Jan 24 '22 at 14:10
  • Just checket ist custom port seems to be included in `Host`-part – Daniel Jan 24 '22 at 14:30
71

If you need this anywhere in your app than you should create a class and add it as a service. Define your static class and your extension method for adding it to the service pipeline like this.

public class MyHttpContext
{
    private static IHttpContextAccessor m_httpContextAccessor;

    public static HttpContext Current => m_httpContextAccessor.HttpContext;

    public static string AppBaseUrl => $"{Current.Request.Scheme}://{Current.Request.Host}{Current.Request.PathBase}";
    
    internal static void Configure(IHttpContextAccessor contextAccessor)
    {
        m_httpContextAccessor = contextAccessor;
    }
}

public static class HttpContextExtensions
{
    public static void AddHttpContextAccessor(this IServiceCollection services)
    {
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    }

    public static IApplicationBuilder UseHttpContext(this IApplicationBuilder app)
    {
        MyHttpContext.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
        return app;
    }
}

It might be a little redundant to expose the HttpContext in this case but I find it very helpful.

You would than add it to the pipeline in your Configfure method which is located in Startup.cs

app.UseHttpContext()

From there it is simple to use it anywhere in your code.

var appBaseUrl = MyHttpContext.AppBaseUrl;
Robert
  • 2,407
  • 1
  • 24
  • 35
  • 7
    This seems to be working if you call MyHttpContext.AppBaseUrl from controller. I need base url in the startup, right after app.UseHttpSettings(). Seems HttpContext is null in there. Any thoughts? Thanks – Whoever Nov 15 '17 at 21:43
  • 2
    Make sure you call services.AddHttpContextAccessor() in the ConfigureServices method. – Robert Nov 16 '17 at 08:22
  • 4
    Thanks. Still the same. I read somewhere that because the way .NET Core are hosted, it won't have the server information until fully started. I was trying to get it from within StartUp. – Whoever Nov 21 '17 at 13:45
  • @Whoever How you solved the issue? I'm stuck over there only. – Sruthi Varghese Jul 08 '19 at 10:16
  • 4
    @SruthiVarghese I don't think you can resolve host url from startup. My understanding is .NET Core are all console app, your startup decides how it's hosted. So the same exact code can run as console, iisexpress, iis, etc. Besides, in PROD, there's often proxy/gateway in front of your servers. I think the easiest way is just create a config value for app base url. – Whoever Jul 08 '19 at 13:59
  • Thanks Rob this is still useful , @Whoever for your solution here, just defined custom port/address: https://stackoverflow.com/a/59347918/7575384 – Mustafa Salih ASLIM Jan 27 '20 at 18:01
28

All of these existing answers depend on an HttpContext object, which is only available during an incoming request. However, I needed to get the URLs in a background service where HttpContext was not available.

This information is also available in the Microsoft.AspNetCore.Hosting.Server.IServer service, as long as the actual host service provides this information. If you're using the default Kestrel server, I've found that it is indeed provided. I have not tested this when hosting IIS in-process or with other hosting models.

You need to get an instance of IServer and then look for the .Features entry of type IServerAddressesFeature. Here's an extension method to get the URL(s) directly from an IServiceProvider:

using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;

public static ICollection<string> GetApplicationUrls(this IServiceProvider services)
{
    var server = services.GetService<IServer>();

    var addresses = server?.Features.Get<IServerAddressesFeature>();

    return addresses?.Addresses ?? Array.Empty<string>();
}

You could however accomplish the same thing by injecting IServer if DI services are available.

Tobias J
  • 19,813
  • 8
  • 81
  • 66
  • 3
    Work well with .NET 6 – Ruxo Feb 16 '22 at 02:03
  • Note that this only works **after** `app.Run()` is called (i.e. in middleware or Background Worker). I tried using this feature to do some stuff before running the website, the array returned is empty. – Luke Vo Jan 29 '23 at 19:55
  • 1
    I also tested with IIS (Express), it lists all registered addresses so you would get 2 items: one `http` and one `https`. Interestingly Kestrel (in VS it shows as `http` and `https`) also has two addresses and it lists the `https` address first. – Luke Vo Jan 29 '23 at 19:58
8
using Microsoft.AspNetCore.Http;    

public void ConfigureServices(IServiceCollection services)
{
     services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
    
    
public AccountController(IHttpContextAccessor httpContextAccessor)
{
     var request = httpContextAccessor.HttpContext.Request;
     var domain = $"{request.Scheme}://{request.Host}";
     //domain => https://varunsoft.in
}
Varun
  • 379
  • 5
  • 10
6

NPNelson answer works if with .Value.ToString()

var baseUrl = $"{this.Request.Scheme}://{this.Request.Host.Value.ToString()}{this.Request.PathBase.Value.ToString()}";
hubert17
  • 285
  • 5
  • 8
1
var baseUrl = Request.GetTypedHeaders().Referer.ToString();

This way you can capture the base url information.

This is how I could get it in Asp .Net Core 3.1 version.

You can access the resource from the link below.

Reference

  • 1
    Thanks for the tip, it probably works in some situtations but it does not work everywhere, i.e startup: app.UseExceptionHandler for instance. Go with $"context.Request.Scheme}://{context.Request.Host}{context.Request.Path}"; – Tomas Hesse Feb 16 '21 at 09:20
-1
string.Format("{0}://{1}{2}", Request.Url.Scheme, Request.Url.Authority, Url.Content("~"));

you can check for more information here: How can I get my webapp's base URL in ASP.NET MVC?

Oren Tamam
  • 33
  • 3