Dmitry Avtonomov
Dmitry Avtonomov

Reputation: 9489

.NET Core 6.0 - Get Kestrel's dynamically bound port (port 0)

Context

The "hosting" mechanism has changed in .NET 6. Previously IWebHost had IWebHost.ServerFeatures property that could be used to get the IServerAddressFeature like so (from this SO answer):

IWebHost host = new WebHostBuilder()
            .UseStartup<Startup>()
            .UseUrls("http://*:0") // This enables binding to random port
            .Build();
host.Start();
foreach(var address in host.ServerFeatures.Get<IServerAddressesFeature>().Addresses) {
  var uri = new Uri(address);
  var port = uri.Port;
  Console.WriteLine($"Bound to port: {port}");
}

Question: how to get the port in .NET 6 with IHost?

Now in .NET 6 I have an IHost. How do I get the port (line with ???):

public class Program {
  public static void Main(string[] args) {
    IHostBuilder hostBuilder = Host.CreateDefaultBuilder(args);
    hostBuilder.ConfigureWebHostDefaults(webHostBuilder => {
      webHostBuilder.ConfigureKestrel(opts => {
        opts.ListenAnyIP(0); // bind web server to random free port
      });
      webHostBuilder.UseStartup<Startup>();
    });
    IHost host = hostBuilder.Build();
    host.Start();
    // If it doesn't fail, at this point Kestrel has started
    // and is listening on a port. It even prints the port to
    // console via logger.
    int boundPort = ???; // some magic GetPort(IHost host) method

    // This link in the docs mentions getting the port, but the example
    // they provide is incomplete
    // https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/endpoints?view=aspnetcore-6.0#port-0
    host.WaitForShutdown();
  }
}

The example from Microsoft's docs:

When the port number 0 is specified, Kestrel dynamically binds to an available port. The following example shows how to determine which port Kestrel bound at runtime:

app.Run(async (context) =>
{
    var serverAddressFeature = context.Features.Get<IServerAddressesFeature>();

    if (serverAddressFeature is not null)
    {
        var listenAddresses = string.Join(", ", serverAddressFeature.Addresses);

        // ...
    }
});

In this example, what is the app? Where can I get that context with .Features?

Upvotes: 5

Views: 2871

Answers (1)

Dmitry Avtonomov
Dmitry Avtonomov

Reputation: 9489

Found a working answer right after posting the question. Function to print the address of a runing web server (including port) in .NET 6. Turns out we're interested in a running instance of a IServer service in the host.

public static void PrintBoundAddressesAndPorts(IHost host)
{
    Console.WriteLine("Checking addresses...");
    var server = host.Services.GetRequiredService<IServer>();
    var addressFeature = server.Features.Get<IServerAddressesFeature>();
    foreach(var address in addressFeature.Addresses)
    {
        var uri = new Uri(address);
        var port = uri.Port;
        Console.WriteLine($"Listing on [{address}]");
        Console.WriteLine($"The port is [{port}]");
    }
}

Found the answer in this article: https://andrewlock.net/finding-the-urls-of-an-aspnetcore-app-from-a-hosted-service-in-dotnet-6/

Upvotes: 6

Related Questions