Determine port Kestrel binded to

偶尔善良 提交于 2019-12-07 01:50:49

问题


I'm writing a simple ASP.NET Core service using ASP.NET Core empty (web) template.

By default, it binds to port 5000 but I would like it to bind to a random available port on the system.

I can do so by modifying BuildWebHost to:

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseUrls("http://*:0") // This enables binding to random port
            .Build();

It binds to a random port but how do I determine from within the application which port I'm listening to?


回答1:


Hosting addresses of ASP.NET Core application could be accessed via IServerAddressesFeature.Addresses collection.

The main challenge is to invoke the code, which will analyze this collection, at the right time. The actual port binding happens when IWebHost.Run() is called (from Program.Main()). Therefore you can't yet access hosting address in Startup.Configure() method, because port has not been yet assigned on this stage. And you loose control after calling IWebHost.Run(), because this call does not return untill web host is shut down.

In my understanding, the most suitable way to analyze bound port is through implementation of IHostedService. Here is working sample:

public class GetBindingHostedService : IHostedService
{
    public static IServerAddressesFeature ServerAddresses { get; set; }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        var address = ServerAddresses.Addresses.Single();
        var match = Regex.Match(address, @"^.+:(\d+)$");
        if (match.Success)
        {
            int port = Int32.Parse(match.Groups[1].Value);
            Console.WriteLine($"Bound port is {port}");
        }

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

In Startup class:

public class Startup
{

    //  ...

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        services.AddSingleton<IHostedService, GetBindingHostedService>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseMvc();

        GetBindingHostedService.ServerAddresses = app.ServerFeatures.Get<IServerAddressesFeature>();
    }
}

Instance of IServerAddressesFeature is passed through ugly static property in GetBindingHostedService. I don't see other way how it could be injected into the service.

Sample Project on GitHub

Overall I'm not happy with such solution. It does the job, however it seems much more complex than it should be.




回答2:


You can call IWebHost.Start() instead of IWebHost.Run() as suggested here. This will allow execution of your Main method to continue so you can get the desired information from IWebHost.ServerFeatures. Just remember, your application will shutdown immediately unless you explicitly tell it not to using IWebHost.WaitForShutdown().

 public static void Main(string[] args)
    {
        var 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}");
        }

        //Tell the host to block the thread just as host.Run() would have.
        host.WaitForShutdown();
    }


来源:https://stackoverflow.com/questions/49871318/determine-port-kestrel-binded-to

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!