How to get ASP.NET Core's local server URLs

This doesn't seem to be a very common use case, but sometimes you need access to the ASP.NET Core server URLs in your Program class. In this case, server URLs does not mean the public URLs that your users see when your website is served on the internet. These URLs are the local URLs that you specify when you run your ASP.NET Core application.
Check out this blog post if you want to get the full public URL of your ASP.NET Core application, or this blog post if you want to generate absolute URLs for your ASP.NET Core application.
There are many ways to configure the URLs ASP.NET Core will try binding to. Here's a couple of ways:
- You can programmatically configure the URLs using the UseUrls method.
- You can use the
--urlsargument like thisdotnet run --urls "http://localhost:8080". - You can configure the URL with the
ASPNETCORE_URLSenvironment variable. - Out of the box, the web templates will create a JSON file Properties/launchSettings.json which holds multiple profiles to run your application. In that JSON file, you can change the
applicationUrlproperty to configure which URL ASP.NET Core will bind to. There are also other ways to set the URLs using JSON configuration.
You could try to programmatically check each one of these variations to see which URLs will be used, but there are multiple built-in APIs you can use to get these URLs.
How to get ASP.NET Core server URLs in Program.cs with minimal APIs #
Here's how you could access the URLs in .NET 6's minimal API Program.cs file:
Here's what the output looks like:
You can get the URLs via the app.Urls property, but only after the application has been started.
From the output, you can see that the URLs collection is empty before the app is started, but once the app is started, the URLs collection is populated.
How to get ASP.NET Core server URLs using Dependency Injection #
You can also get the URLs in any class configured with dependency injection. Here's an example using a Controller:
You can have the IServer object injected through your constructor, and then get the IServerAddressesFeature feature.
This IServerAddressesFeature has an Addresses property which is a collection of the ASP.NET Core URLs. This is actually what app.Urls uses in the previous example.
Any time the Index action is called, the following line is written to the console:
The Addresses collection will be empty if the server application hasn't started yet. You don't have to worry about that inside of a Controller because the controllers won't be invoked until the server has been started yet. But if you try to get address outside of Razor Pages, MVC controllers, or endpoints, you'll have to make sure the server has been started before getting the URLs.
So, what do you do if the server application hasn't started yet? Let's look at another example.
How to get ASP.NET Core server URLs in IHostedService or BackgroundService #
In some cases, you need to access the ASP.NET Core URLs outside of the Razor Pages, Controllers, or endpoints. In this case, there's no certainty the web server has been started, and no certainty the addresses collection is populated.
Luckily, there's another built-in API that can help us, the IHostApplicationLifetime. Let's take a look at an IHostedService example:
You can receive an instance of IHostApplicationLifetime through constructor dependency injection, and then use it in StartAsync to hook into its lifecycle events.
Oddly enough, the lifecycle events aren't C# events, but instead they are of type CancellationToken.
To run code once the application has started, you can pass in a lambda or delegate to hostApplicationLifetime.ApplicationStarted.Register.
The output looks like this:
You can also do this in the ExecuteAsync method if you use a BackgroundService:
In BackgroundService.ExecuteAsync you can properly wait by awaiting a task. Unfortunately, in IHostedService.StartAsync you cannot do this because the application builder will wait for your hosted service to start, but your hosted service would wait for the application to start, in which case neither ever finish starting.
This is probably a less common use case, but being able to access the URLs can come in really useful. For example, you can use these URLs to automatically start a ngrok tunnel and use the tunnel to respond to webhooks which I wrote about for the Twilio blog!
I'm curious to know how you'll be using these URLs, let me know!