.NET Core 实战 [No.326~329] Web 主机配置
默认配置
ASP.NET Core Web 应用程序 项目创建时,已经提供了一些默认配置。
随着版本的升级,创建 WebHost 的方式也一直在更新。 .NET Core 1.1 时默认的写法如下:
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
的过程。
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.json 和 appsettings.{启动环境}.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
静态类来实现,其默认代码如下:
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 的生命周期,这会造成资源的浪费,特别是当使用数据库链接等资源的时候。
一种解决方法是在启动时禁用 作用域验证 :
csharppublic 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 的方法有很多种,比较常用的有以下三种:
调用 UseUrls 方法
UseUrls 方法支持多个参数。
csharpWebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .UseUrls("http://localhost:6500", "http://localhost:7000", "http://*:7500") .Build() .Run();
在 APS.NET Core 3.1 中配置方法如下:
csharppublic 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"); });
调用 UseSetting 方法
监听多个端口时,使用半角分号
;
分隔。csharpWebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .UseSetting(WebHostDefaults.ServerUrlsKey, "http://localhost:8990;http://localhost:46133") .Build() .Run();
在 APS.NET Core 3.1 中配置方法如下:
csharppublic 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"); });
通过配置文件
在 ASP.NET Core 2.1 中首先要添加 json 文件,假设名为 host.json 。
json{ "urls": "http://localhost:3600;http://*:8080" }
然后修改 Main 方法:
csharpvar 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):
csharpWebHost.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": "*" }
再补充一个书中没有提到的方法:通过命令行参数。
dotnet run --urls "http://*:8090;http://localhost:9090"
另外还可以通过环境变量(ASPNETCORE_URLS)配置 URL。
Kestrel
Kestrel 是 ASP.NET Core 框架的默认 Web 服务器组件。WebHost.CreateDefaultBuilder 方法的默认配置中已经默认使用了 Kestrel,但若是自己编写代码来创建 Web 服务器,则可以通过 UseKestrel 扩展方法启用 Kestrel 。另外可以通过 UseIISIntegration 方法启用 IIS 集成功能,只有在使用 IIS 作为反向代理时才有效。
new WebHostBuilder()
.UseStartup<Startup>()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel()
.UseIISIntegration()
.Build()
.Run();
配置调试方案
ASP.NET Core Web 项目自带两个调试方案:
- 以 IIS Express 作为反向代理运行应用程序
- 用项目名称命名,独立运行项目(通过 dotnet 命令执行)
可以在 项目属性 → 调试 页面新增/修改/删除调试方案。配置完成后可以在 Properties\launchSettings.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 个精彩案例》 -- 周家安 著