【问题标题】:Determine port Kestrel binded to确定 Kestrel 绑定到的端口
【发布时间】:2018-09-27 00:28:50
【问题描述】:

我正在使用 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();

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

【问题讨论】:

标签: asp.net-core asp.net-core-2.0 kestrel-http-server


【解决方案1】:

可以通过IServerAddressesFeature.Addresses集合访问ASP.NET Core应用的托管地址。

主要挑战是调用代码,该代码将在正确的时间分析此集合。实际的端口绑定发生在调用IWebHost.Run() 时(来自Program.Main())。因此,您还不能在Startup.Configure() 方法中访问主机地址,因为此阶段尚未分配端口。并且在调用IWebHost.Run() 后您会失去控制,因为此调用在虚拟主机关闭之前不会返回。

据我了解,分析绑定端口最合适的方法是通过实现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>();
    }
}

IServerAddressesFeature 的实例通过 GetBindingHostedService 中的丑陋静态属性传递。我看不出有其他方式可以将它注入到服务中。

Sample Project on GitHub

总的来说,我对这样的解决方案不满意。它完成了这项工作,但它似乎比应有的复杂得多。

【讨论】:

  • 为我工作。但是我很惊讶你需要通过这么多的圈子才能获得这样的基本信息。
  • 行为似乎在 .NET Core 3.1 中发生了变化。从外观上看,此解决方案会导致 NullReferenceException。创建单例时,StartAsync() 被正确调用,但 ServerAddresses.Addresses 为空。
  • 在 .Net Core 2 中,IHostedService 在主机启动后执行,因此服务器地址信息随时可用。在 .Net Core 3 中,IHostedService 在主机构建之后运行,但在主机启动并且地址不再可用之前运行。 See here
【解决方案2】:

您可以调用IWebHost.Start() 而不是IWebHost.Run() 建议here。这将允许您继续执行您的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 似乎也在做其他一些事情。
猜你喜欢
  • 1970-01-01
  • 2010-09-25
  • 1970-01-01
  • 2013-05-19
  • 2016-01-17
  • 1970-01-01
  • 1970-01-01
  • 2012-08-04
  • 2021-11-15
相关资源
最近更新 更多