NetCore 启动地址配置详解 UseUrls("xxxx") > 环境变量 > appsetting.json配置urls>默认地址

发布时间 2023-05-28 00:11:22作者: sunny123456

NetCore 启动地址配置详解
原文链接:https://blog.51cto.com/u_15127582/2781067

背景

程序在发布部署时候,设置环境ASPNETCORE_URLS不生效,也没在代码里使用UseUrls("xxxx"),启动一直是http://localhost:5000.最后测试发现只有在appsettings.json中配置urls才生效,网上找了半天资料也没看到有什么问题。

最终翻看源代码,发现是在StartUp中的Configure替换了全局IConfiguration导致。

平时开发大体知道程序启动时候端口启用顺序是
UseUrls("xxx")> 环境变量 > 默认,具体是怎么确定使用哪个配置的,没找到资料,所有才有了本文。

启动地址配置的几种方式介绍
  1. 环境变量ASPNETCORE_URLS
#windows 
set ASPNETCORE_URLS=http://localhost:6000
#linux 
export ASPNETCORE_URLS=http://localhost:6000
  • 1.
  • 2.
  • 3.
  • 4.
  1. UseUrls("http://localhost:6000")
  2. appsettings.json新增urls或者server.urls配置
{
    "urls":"http://localhost:6000;http://localhost:6001",
    "server.urls":"http://localhost:6000;http://localhost:6001"
}
  • 1.
  • 2.
  • 3.
  • 4.
  1. 使用系统默认
说明

程序启动过程中,一个配置key会重复使用,先放这里

//WebHostDefaults.ServerUrlsKey如下
public static readonly string ServerUrlsKey = "urls";
  • 1.
  • 2.
Web项目启动地址配置说明

今天是介绍启动方式,所以web启动流程不是重点。直接进入正题。

Web启动最终是调用WebHost.StartAsync,源代码在这WebHost。其中有个方法EnsureServer来获取启动地址

private static readonly string DeprecatedServerUrlsKey = "server.urls";

//省略
var urls = _config[WebHostDefaults.ServerUrlsKey] ?? _config[DeprecatedServerUrlsKey];

  • 1.
  • 2.
  • 3.
  • 4.

是从全局IConfigration实例中获取启动地址。所以我的遇到问题这里就解决了。但环境变量和UseUrls是如何解析并记载进来的呢?下面就开今天讲解。

环境变量配置详解

一般Web程序启动代码如下:

Host.CreateDefaultBuilder(args)
   .ConfigureWebHostDefaults(webBuilder =>
   {
       webBuilder.UseStartup();
   }).Build().Run();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

其中ConfigureWebHostDefaults的会用调用扩展方法ConfigureWebHost

public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Actionconfigure)
 {
     return builder.ConfigureWebHost(webHostBuilder =>
     {
         WebHost.ConfigureWebDefaults(webHostBuilder);

         configure(webHostBuilder);
     });
 }

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

以上代码都是定义在Microsoft.Extensions.Hosting中。

继续看ConfigureWebHost代码,这个方法就定义在Microsoft.AspNetCore.Hosting程序集中了。

public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Actionconfigure)
{
    //这里会加载环境变量
    var webhostBuilder = new GenericWebHostBuilder(builder);
    //这里会调用UseUrls等扩展方法
    configure(webhostBuilder);
    builder.ConfigureServices((context, services) => services.AddHostedService());
    return builder;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

在GenericWebHostBuilder 构造函数里有如下代码,用来初始化配置,最终添加到全局
IConfiguration实例中,也就是Host中IConfiguration实例。

builder.ConfigureServices((context, services) => services.AddHostedService

//加入环境变量配置
config = new ConfigurationBuilder()
           .AddEnvironmentVariables(prefix: "ASPNETCORE
")
           .Build();
//把配置加载到Host
_builder.ConfigureHostConfiguration(config =>
{
    config.AddConfiguration(_config);

    // We do this super early but still late enough that we can process the configuration
    // wired up by calls to UseSetting
    ExecuteHostingStartups();
})

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

AddEnvironmentVariables环境变量解析最终会使用EnvironmentVariablesConfigurationProvider,有兴趣的可以看下AddEnvironmentVariables源代码,EnvironmentVariablesConfigurationProvider解析环境的方法如下。

public override void Load()
{
    Load(Environment.GetEnvironmentVariables());
}

internal void Load(IDictionary envVariables)
{
    var data = new Dictionary(StringComparer.OrdinalIgnoreCase);
    //这里是筛选ASPNETCORE_开头的环境变量
    var filteredEnvVariables = envVariables
        .Cast()
        .SelectMany(AzureEnvToAppEnv)
        .Where(entry => ((string)entry.Key).StartsWith(_prefix, StringComparison.OrdinalIgnoreCase));

    foreach (var envVariable in filteredEnvVariables)
    {
        //这里会把前缀去掉加到配置里
        var key = ((string)envVariable.Key).Substring(_prefix.Length);
        data[key] = (string)envVariable.Value;
    }

    Data = data;
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

IConfiguration中的key是不区分大小写的,所有最终的效是在全局IConfiguration中新增一条key为urls的记录。
而如果使用默认Host.CreateDefaultBuilder(),appsettings.json中的配置会先加载。
如果在appsettings.json中配置urls的话,环境变量也定义了,就会被环境变量的覆盖掉。

UseUrls解析

UseUrls解析最终会调用GenericWebHostBuilder中的UseSetting

//UseUrls代码如下
public static IWebHostBuilder UseUrls(this IWebHostBuilder hostBuilder, params string[] urls)
{
    if (urls == null)
    {
        throw new ArgumentNullException(nameof(urls));
    }

    return hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, string.Join(ServerUrlsSeparator, urls));
}

//GenericWebHostBuilder中的UseSetting
public IWebHostBuilder UseSetting(string key, string value)
{
    _config[key] = value;
    return this;
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

由于这个方法是在 new GenericWebHostBuilder(builder);
之后调用,就是 configure(webhostBuilder);,上面代码也有说明。所以IConfiguration中urls如果有值,又会被覆盖掉。所以优先级最高的是UseUrls()。

默认地址

假如以上3种配置都没有,就是地址为空,会使用默认策略。这里是源代码,下面是默认策略使用的地址

 ////// The endpoint Kestrel will bind to if nothing else is specified.
 ///public static readonly string DefaultServerAddress = "http://localhost:5000";

 ////// The endpoint Kestrel will bind to if nothing else is specified and a default certificate is available.
 ///public static readonly string DefaultServerHttpsAddress = "https://localhost:5001";

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
结论
  1. 启动端口设置优先级如下:
    UseUrls("xxxx") > 环境变量 > appsetting.json配置urls>默认地址
  2. 不要随意替换全局的IConfiguration,如果不手动加入环境变量解析的话,会丢失一部分配置数据。
  3. 将自己的配置注入的全局,可以使用以下方式,这样就会把配置追加到全局的IConfiguration中
 Host.CreateDefaultBuilder(args)
     .ConfigureWebHostDefaults(builder =>
     {
         builder.UseStartup();
     }).ConfigureAppConfiguration(config =>
     {
         config.AddJsonFile("config.json", true, true);
     }).Build().Run();
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.