Skip to content

.NET Core 实战 [No.326~329] Web 主机配置

默认配置

ASP.NET Core Web 应用程序 项目创建时,已经提供了一些默认配置。

随着版本的升级,创建 WebHost 的方式也一直在更新。 .NET Core 1.1 时默认的写法如下:

csharp
public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .UseApplicationInsights()
        .Build();

    host.Run();
}

.NET Core 2.1 中则改成了使用 WebHost 静态类封装了创建 WebHostBuilder 的过程。

csharp
public static void Main(string[] args)
{
    CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>();

WebHost.CreateDefaultBuilder 方法使用如下的默认配置来创建 Web 主机(摘自其方法的备注):

The following defaults are applied to the returned Microsoft.AspNetCore.Hosting.WebHostBuilder:

  • use Kestrel as the web server and configure it using the application's configuration providers,
    使用内置的 Kestrel 服务器组件
  • set the Microsoft.AspNetCore.Hosting.IHostingEnvironment.ContentRootPath to the result of System.IO.Directory.GetCurrentDirectory,
    将应用程序的当前目录作为 Web 内容的根目录
  • load Microsoft.Extensions.Configuration.IConfiguration from 'appsettings.json' and 'appsettings.[Microsoft.AspNetCore.Hosting.IHostingEnvironment.EnvironmentName].json',
    appsettings.jsonappsettings.{启动环境}.json 文件加载配置
  • load Microsoft.Extensions.Configuration.IConfiguration from User Secrets when Microsoft.AspNetCore.Hosting.IHostingEnvironment.EnvironmentName is 'Development' using the entry assembly,
    当启动环境为 Development 时从 User Secrets用户机密)加载配置
  • load Microsoft.Extensions.Configuration.IConfiguration from environment variables,
    从环境变量加载配置
  • load Microsoft.Extensions.Configuration.IConfiguration from supplied command line args,
    从命令行参数加载配置
  • configures the Microsoft.Extensions.Logging.ILoggerFactory to log to the console and debug output,
    配置日志输出到命令行窗口和调试窗口
  • enables IIS integration,
    启用 IIS 交互(IIS 将作为反向代理端,将 HTTP 请求转发到 Web 应用程序)
  • and enables the ability for frameworks to bind their options to their default configuration sections.

.NET Core 3.1 中则改成使用 Host 静态类来实现,其默认代码如下:

csharp
public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Host.CreateDefaultBuilder 方法使用如下的默认配置来创建 Web 主机(摘自其方法备注):

The following defaults are applied to the returned Microsoft.Extensions.Hosting.HostBuilder:

  • set the Microsoft.Extensions.Hosting.IHostEnvironment.ContentRootPath to the result of System.IO.Directory.GetCurrentDirectory
  • load host Microsoft.Extensions.Configuration.IConfiguration from "DOTNET_" prefixed environment variables
    DOTNET_ 前缀的环境变量加载主机配置
  • load host Microsoft.Extensions.Configuration.IConfiguration from supplied command line args
  • load app Microsoft.Extensions.Configuration.IConfiguration from 'appsettings.json' and 'appsettings.[Microsoft.Extensions.Hosting.IHostEnvironment.EnvironmentName].json'
  • load app Microsoft.Extensions.Configuration.IConfiguration from User Secrets when Microsoft.Extensions.Hosting.IHostEnvironment.EnvironmentName is 'Development' using the entry assembly
  • load app Microsoft.Extensions.Configuration.IConfiguration from environment variables
  • load app Microsoft.Extensions.Configuration.IConfiguration from supplied command line args
  • configure the Microsoft.Extensions.Logging.ILoggerFactory to log to the console, debug, and event source output
  • enables scope validation on the dependency injection container when Microsoft.Extensions.Hosting.IHostEnvironment.EnvironmentName is 'Development'
    当启动环境为 Development 时在依赖注入容器上启用 作用域验证

相比 .NET Core 2.1 多了两项:

  • DOTNET_ 前缀的环境变量加载配置

    ASP.NET Core Web 应用程序默认的环境变量是 ASPNETCORE_ 前缀的,这两个前缀倒是不知道有什么区别。

  • 当启动环境为 Development 时在依赖注入容器上启用 作用域验证

    若在程序的启动过程中(如 StartUp.Configure)解析 Scoped 生命周期的服务,运行时就会报 InvalidOperationException 异常。

    System.InvalidOperationException:“Cannot resolve scoped service 'WebApplication1.ConcreteA' from root provider.”

    之所以会引发上面的异常,原因在于使用 Scoped 生命周期时,正常的作用域是一次 HTTP 请求,请求结束时会释放掉实例。而在 启动过程 的 root provider 中解析服务时,其生命周期一般相当于整个 Application 的生命周期,这会造成资源的浪费,特别是当使用数据库链接等资源的时候。

    一种解决方法是在启动时禁用 作用域验证

    csharp
    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseDefaultServiceProvider(options => options.ValidateScopes = false)
            .UseStartup<Startup>();

    上面的方法并不好,只是不做验证,并没有解决资源占用问题。最好是手动指定一个 Scope ,在指定的 Scope 结束时释放资源(摘自 StackOverflow)。

    csharp
    // Create a new IServiceScope that can be used to resolve scoped services.
    using (var scope = app.ApplicationServices.CreateScope())
    {
        // resolve the services within this scope
        ConcreteA A = scope.ServiceProvider.GetRequiredService<ConcreteA>();
    
        //ConcreteA instance and injected ConcreteB are used in the same scope
    
        //do something
        A.Run();
    }
    
    //both will be properly disposed of here when they both got out of scope.

配置 URL

指定 URL 的方法有很多种,比较常用的有以下三种:

  1. 调用 UseUrls 方法

    UseUrls 方法支持多个参数。

    csharp
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseUrls("http://localhost:6500", "http://localhost:7000", "http://*:7500")
        .Build()
        .Run();

    APS.NET Core 3.1 中配置方法如下:

    csharp
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseDefaultServiceProvider(options => options.ValidateScopes = false)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
                webBuilder.UseUrls("http://localhost:6500", "http://localhost:7000", "http://*:7500");
            });
  2. 调用 UseSetting 方法

    监听多个端口时,使用半角分号 ; 分隔。

    csharp
    WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseSetting(WebHostDefaults.ServerUrlsKey, "http://localhost:8990;http://localhost:46133")
        .Build()
        .Run();

    APS.NET Core 3.1 中配置方法如下:

    csharp
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseDefaultServiceProvider(options => options.ValidateScopes = false)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
                webBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, "http://localhost:8990;http://localhost:46133");
            });
  3. 通过配置文件

    ASP.NET Core 2.1 中首先要添加 json 文件,假设名为 host.json

    json
    {
        "urls": "http://localhost:3600;http://*:8080"
    }

    然后修改 Main 方法:

    csharp
    var builder = WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>();
    var config = new ConfigurationBuilder().SetBasePath(builder.GetSetting(WebHostDefaults.ContentRootKey))
        .AddJsonFile("host.json");
    builder.UseConfiguration(config.Build());
    builder.Build().Run();

    这里我有个感觉比较疑惑的地方,上面的 urls 如果放在 appsettings.json 文件中并没有默认就被加载。按照上面一节中对 WebHost.CreateDefaultBuilde 方法的描述,appsettings.json 应该是会被默认加载的才对。 StackOverflow 上也有一个类似的问题,可惜回答中只有一个通过配置 Kestrel 来设置 URL 的方式,而且貌似只支持设置一个端口。

    json
    "Kestrel": {
        "EndPoints": {
            "Http": {
                "Url": "http://localhost:5000"
            }
        }
    }

    对应的通过代码设置 Kestrel 的方式如下(摘自 StackOverflow):

    csharp
    WebHost.CreateDefaultBuilder(args)
        .UseKestrel(options =>
        {
            options.Listen(IPAddress.Loopback, 5000);  // http:localhost:5000
            options.Listen(IPAddress.Any, 80);         // http:*:80
            options.Listen(IPAddress.Loopback, 443, listenOptions =>
            {
                listenOptions.UseHttps("certificate.pfx", "password");
            });
        })
        .UseStartup<Startup>()
        .Build();

    APS.NET Core 3.1 中则不需要新增 json 文件,也不需要修改 main 方法,直接在 appsettings.json 文件中添加一个 Urls 就行了。

    json
    {
        "Urls": "http://localhost:3600;http://*:8099",
        "Logging": {
            "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
            }
        },
        "AllowedHosts": "*"
    }

再补充一个书中没有提到的方法:通过命令行参数。

bash
dotnet run --urls "http://*:8090;http://localhost:9090"

另外还可以通过环境变量(ASPNETCORE_URLS)配置 URL。

Kestrel

KestrelASP.NET Core 框架的默认 Web 服务器组件。WebHost.CreateDefaultBuilder 方法的默认配置中已经默认使用了 Kestrel,但若是自己编写代码来创建 Web 服务器,则可以通过 UseKestrel 扩展方法启用 Kestrel 。另外可以通过 UseIISIntegration 方法启用 IIS 集成功能,只有在使用 IIS 作为反向代理时才有效。

csharp
new WebHostBuilder()
    .UseStartup<Startup>()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseKestrel()
    .UseIISIntegration()
    .Build()
    .Run();

配置调试方案

ASP.NET Core Web 项目自带两个调试方案:

  • 以 IIS Express 作为反向代理运行应用程序
  • 用项目名称命名,独立运行项目(通过 dotnet 命令执行)

可以在 项目属性 → 调试 页面新增/修改/删除调试方案。配置完成后可以在 Properties\launchSettings.json 文件看到保存的配置内容。大致结构如下:

json
{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:65132",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "WebApplication2": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "http://localhost:5000"
    }
  }
}

commandName 一共有 4 个可选值

  • IIS Express:以 IIS Express 作为反向代理进程
  • IIS:以 IIS 服务(完整版 IIS)作为反向代理进程
  • Project:使用 dotnet 命令直接运行应用程序
  • Executable:自定义一个可执行文件,这种调试方案一般不常用

environmentVariables 是环境变量的键值对,名称以 ASPNETCORE_ 为前缀,后跟环境变量名。
Environment 环境变量有 3 个预定义的值:

  • Development:开发环境
  • Staging:预览环境
  • Production:生产环境

参考:《.NET Core 实战:手把手教你掌握 380 个精彩案例》 -- 周家安 著


Page Layout Max Width

Adjust the exact value of the page width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the page layout
A ranged slider for user to choose and customize their desired width of the maximum width of the page layout can go.

Content Layout Max Width

Adjust the exact value of the document content width of VitePress layout to adapt to different reading needs and screens.

Adjust the maximum width of the content layout
A ranged slider for user to choose and customize their desired width of the maximum width of the content layout can go.