确定Kestrel绑定的端口。

11

我正在使用ASP.NET Core空(web)模板编写一个简单的ASP.NET Core服务。

默认情况下,它绑定到端口5000,但我想将其绑定到系统上可用的随机端口。

我可以通过修改BuildWebHost来实现:

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseUrls("http://*:0") // This enables binding to random port
            .Build();
它会绑定到一个随机端口,但是我如何确定应用程序正在监听哪个端口?

它会绑定到一个随机端口,但是我如何确定应用程序正在监听哪个端口?


1
运行已经有一个如何做到这一点的示例内置在其中:https://github.com/aspnet/Hosting/blob/a63932a492513cdeb4935661145084cad2ae5521/src/Microsoft.AspNetCore.Hosting/WebHostExtensions.cs#L104 - Tratcher
2个回答

7
ASP.NET Core应用程序的托管地址可以通过IServerAddressesFeature.Addresses集合访问。
主要挑战是在正确的时间调用分析此集合的代码。实际端口绑定发生在调用IWebHost.Run()(从Program.Main())时。因此,在Startup.Configure()方法中还不能访问托管地址,因为在此阶段尚未分配端口。而且,在调用IWebHost.Run()之后,您将失去控制,因为此调用直到Web主机关闭后才返回。
在我看来,分析绑定端口的最合适方式是通过实现IHostedService。这里是一个工作示例:
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;
    }
}

Startup类中:
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>();
    }
}

GetBindingHostedService中,通过丑陋的静态属性传递IServerAddressesFeature实例。我没有看到其他注入服务的方法。 Github上的示例项目 总的来说,我对这样的解决方案不太满意。虽然它能完成工作,但它似乎比应该的复杂得多。

5
对我来说起作用了。但我很惊讶你需要经过这么多步骤才能获得这么基本的信息。 - Regent
行为似乎在.NET Core 3.1中发生了变化。从外观上看,这个解决方案会导致NullReferenceException。当单例被创建时,StartAsync()被正确调用,但是ServerAddresses.Addresses为空。 - Manfred
1
在 .Net Core 2 中,IHostedService 在主机启动后执行,因此服务器地址信息是随时可用的。在 .Net Core 3 中,IHostedService 在主机构建后运行,但在主机启动之前运行,因此地址不再可用。请参见此处 - Preston S
你可以注入一个IServer并调用server.Features.GetFeature<IServerAddressFeature>() - Kyle McClellan

6

您可以调用IWebHost.Start()而不是建议中的IWebHost.Run(),如此处所示。这将允许执行您的Main方法以便您可以从IWebHost.ServerFeatures获取所需信息。只需记住,除非您使用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();
    }

这看起来也是一个不错的方法 - 尽管似乎“Run”还在做一些其他的事情。 - Regent

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接