翻译 - ASP.NET Core 基本知识 - Web 主机 (Web Host)

翻译自 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/web-host?view=aspnetcore-5.0web

ASP.NET Core 应用程序配置和启动一个 Host。Host 负责应用程序的启动和生命周期的管理。至少的,Host 配置一个服务器和一个请求处理管道。Host 也会设置日志,依赖注入和配置。json

这篇文章覆盖了 Web Host,任然是只是向后兼容可用。 Generic Host 推荐用于全部类型的应用程序。api

配置一个 Host

使用 IWebHostBuilder 的实例建立一个 Host。通常会在应用程序的入口点 Main 方法中建立。浏览器

在工程模板中,Main 方法位于 Program.cs 中。典型的应用程序调用 CreateDefaultBuilder 启动配置一个 Host:服务器

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

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

代码中调用 CreateDefaultBuilder 的是在一个叫作 CreateWebHostBuilder 的方法中,这个方法从 Main 中分离出来,在 Main 方法中在 builder 对象上调用 Run 方法。这中分离是有必须的,若是你使用了 Entity Framework Core tools。这个工具指望找到一个 CreateWebHostBuilder 方法,以便能没必要运行应用程序就可以在设计的时候配置 Host。一种途径是实现接口 IDesignTimeDbContextFactory。更多信息,查看 Design-time DbContext Creationapp

CreateDefaultBuilder 执行了如下任务:框架

CreateDefaultBuilder 定义的配置能够被覆盖和使用 ConfigureAppConfigurationConfigureLogging 和其它方法及 IWebHostBuilder 的扩展方法扩展。下面是几个例子:async

  • ConfigureAppConfiguration 用来为应用程序指定额外的 IConfiguration。下面的 ConfigureAppConfiguration 调用添加了一个代理去包含在 appsettings.xml 文件中的应用程序的配置。ConfigureAppConfiguration 可能会调用屡次。注意这个配置并不该用到主机(例如,服务器 URLs 或者 环境)。查看 Host configuration values 部分。
    WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.AddXmlFile("appsettings.xml", optional: true, reloadOnChange: true);
        })
        ...
  • 下面的 ConfigureLogging 调用添加一个代理去配置最小的logging level (SetMinimumLevel) 为 LogLevel.Warning。这个设置覆盖了appsettings.Development.json (LogLevel.Debug) 和 appsettings.Production.json (LogLevel.Error) 里面经过 CreateDefaultBuilder 配置的设置。ConfigureLogging 可能会调用屡次。
    WebHost.CreateDefaultBuilder(args)
        .ConfigureLogging(logging => 
        {
            logging.SetMinimumLevel(LogLevel.Warning);
        })
        ...
  • 下面的 ConfigureKestrel 的调用覆盖了 Kestrel 经过 CreateDefaultBuilder 配置的默认的 Limits.MaxRequestBodySize 30,000,000 字节:
    WebHost.CreateDefaultBuilder(args)
        .ConfigureKestrel((context, options) =>
        {
            options.Limits.MaxRequestBodySize = 20000000;
        });

    content root 决定了 Host 在哪里搜索内容文件,例如 MVC 视图文件。当应用程序从工程根目录启动的时候,工程根目录被用做内容根目录。这在 Visual Studio 和 dotnet new templates 默认使用。ide

更多关于应用程序配置的信息,查看 Configuration in ASP.NET Core工具

注意

做为一种使用静态 CreateDefaultBuilder 方法的途径,从 WebHostBuilder 建立一个 Host 在 ASP.NET Core 2.x 中是受支持的。

当设置一个主机的时候,能够提供 Configure 和 ConfigureServices 这两个方法。若是一个 Startup 方法被指定了,它必须定义 Configure 方法。更多信息,查看 App startup in ASP.NET Core 。屡次调用 ConfigureServices 将会附加到另一个上面。在 WebHostBuilder 上屡次调用 Configure 或者 UseStartup 将会替换以前的设置。

Host configuration values

WebHostBuilder 依赖下面的方法设置主机配置值:

  • Host builder configuration,包含格式为 ASPNETCORE_{configurationKey} 的环境变量。例如,ASPNETCORE_ENVIRONMENT。
  • 扩展,例如 UseContentRoot 和 UseConfiguration (查看 Override configuration 部分)
  • UseSetting 和相关的键。当使用 UseSetting 设置值得时候,值被设置为字符串而忽略它的类型。

主机使用最后设置值得选项。更多信息查看,Override configuration

Application Key (Name)

当 UseStartup 或者 Configure 在主机构造方法中调用的时候,IWebHostEnvironment.ApplicationName 属性会自动设置。值被设置为包含应用程序入口点的程序集的名称。显式的设置,可使用 WebHostDefaults.ApplicationKey

Key: applicationName

Type: string

Default: 包含应用程序入口点程序集的名称

Set using: UseSetting

Environment variable: ASPNETCORE_APPLICATIONNAME

WebHost.CreateDefaultBuilder(args)
    .UseSetting(WebHostDefaults.ApplicationKey, "CustomApplicationName")

Capture Startup Errors

设置捕获启动错误的控制

Key: captureStartupErrors

Type: bool (true or 1)

Default: 默认为 false,除非应用程序使用 Kestrel 运行在 IIS 以后,这时默认是 true

Set using: CaptureStartupErrors

Environment variable: ASPNETCORE_CAPTURESTARTUPERRORS

当设置为 false 时,启动过程当中的错误会致使主机退出。当设置为 true 时,主机会捕获启动过程当中的异常,而且试图启动服务器。

WebHost.CreateDefaultBuilder(args)
    .CaptureStartupErrors(true)

Content root

这个设置决定了 ASP.NET Core 开始搜索内容文件的位置。

Key: contentRoot

Type: string

Default: 默认是应用程序程序集所在的目录

Set using: UseContentRoot

Environment variable: ASPNETCORE_CONTENTROOT

content root 也被用做 web root 的基本路径。若是 content root 路径不存在,主机就会启动失败。

WebHost.CreateDefaultBuilder(args)
    .UseContentRoot("c:\\<content-root>")

更多信息,请查看:

Detailed Errors

决定是否应该详细错误信息

Key: detailedErrors

Type: bool (treu 或者 1)

Default: false

Set using: UseSetting

Environment variable: ASPNETCORE_DETAILEDERRORS

当使能的时候(或者 Environment 被设置为 Development 的时候),应用程序会捕获异常详细信息。

WebHost.CreateDefaultBuilder(args)
    .UseSetting(WebHostDefaults.DetailedErrorsKey, "true")

Environment

 设置应用程序环境

Key: environment

Type: string

Default: Production

Set using: UseEnvironment

Environment variable: ASPNETCORE_ENVIRONMENT

environmenmt 能够被设置为任意的值。框架定义的值包括 Development,Staging 和 Production。值不区分大小写。默认的,Environment 从 ASPNETCORE_ENVIRONMENT  环境变量中读取。当使用 Visual Studio 时,环境变量可能在 lauchSetting.json 文件中设置。更过信息,请查看: Use multiple environments in ASP.NET Core

WebHost.CreateDefaultBuilder(args)
    .UseEnvironment(EnvironmentName.Development)

Hosting Startup Assemblies

设置应用程序托管启动程序集

Key: hostingStartupAssemblies

Type: string

Default: Empty string

Set using: UseSetting

Environment variable: ASPNETCORE_HOSTINGSTARTUPASSEMBLIES

以逗号分隔的字符串,启动时加载的托管的启动程序集

尽管配置值被设置为空字符串,托管程序集老是包含应用程序程序集。当提供了托管启动程序集,它们在应用程序启动时建立公共服务时被添加到应用程序程序集。

WebHost.CreateDefaultBuilder(args)
    .UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "assembly1;assembly2")

HTTPs Port

设置 HTTPS 重定向端口。使用 enforcing HTTPS

Key: https_port

Type: string

Defalut: 默认无设置

Set using: UseSetting

Environment variable: ASPNETCORE_HTTPS_PORT

WebHost.CreateDefaultBuilder(args)
    .UseSetting("https_port", "8080")

Hosting Startup Exclude Assemblies

冒号分隔的字符串,启动时排除托管启动程序集

Key: hostingStartupExcludeAssemblies

Type: string

Default: Empty string

Set using: UseSetting

Environment variable: ASPNETCORE_HOSTINGSTARTUPEXCLUDEASSEMBLIES

WebHost.CreateDefaultBuilder(args)
    .UseSetting(WebHostDefaults.HostingStartupExcludeAssembliesKey, "assembly1;assembly2")

Prefer Hosting URLs

代表主机是否应该在使用 WebHostBuilder 配置的 URLs 上监听,而不是 IServer 实现配置的 URLs

Key: preferHostingUrls

Type: bool (true 或者 1)

Default: true

Set using: PreferHostingUrls

Environment variable: ASPNETCORE_PREFERHOSTINGURLS

WebHost.CreateDefaultBuilder(args)
    .PreferHostingUrls(false)

Prevent Hosting Startup

阻止自动加载托管启动程序集,包括应用程序程序集配置的托管启动程序集。更多信息查看,Use hosting startup assemblies in ASP.NET Core

Key: preventHostingStartup

Type: bool (true 或者 1)

Default: false

Set using: UseSetting

Environment variable: ASPNETCORE_PREVENTHOSTINGSTARTUP

WebHost.CreateDefaultBuilder(args)
    .UseSetting(WebHostDefaults.PreventHostingStartupKey, "true")

Server URLs

代表带有端口和协议的 IP 地址或者主机地址是否应该被服务器监听请求

Key: urls

Type: string

Default: http://localhost:5000

Set using: UseUrls

Environment variable: ASPNETCORE_URLS

设置一组服务器应该响应的冒号(;)分隔的 URL 前缀。例如,http://localhost:123。使用 "*" 代表服务器是否应该监放任意使用特定端口和协议(例如,http://*:5000)的 IP 地址或者主机地址。协议 (http:// 或者 https://) 必须包含在每个 URL 中。支持的格式因服务器不一样而不一样。

WebHost.CreateDefaultBuilder(args)
    .UseUrls("http://*:5000;http://localhost:5001;https://hostname:5002")

Kestrel 有它本身的 endpoint 配置 API。更多信息查看,Configure endpoints for the ASP.NET Core Kestrel web server

Shutdown Timeout

指定等待 Web Host 关闭的超时时间

Key: shutdownTimeoutSeconds

Type: int

Default: 5

Set using: UseShutdownTimeout

Environment variable: ASPNETCORE_SHUTDOWNTIMEOUTSECONDS

尽管使用 UseSetting 能够接受键为 int 的值(例如,.UseSetting(WebHostDefaults.ShutdownTimeoutKey,"10")),UseShutdownTimeout 带有 TimeSpan 参数。

在超时时间内,主机会:

若是在全部服务中止以前超时了,任何活动的服务在应用程序关闭时都会中止。即便服务没有完成处理也会被中止。若是服务须要更多的时间去中止,增长超时时间。

WebHost.CreateDefaultBuilder(args)
    .UseShutdownTimeout(TimeSpan.FromSeconds(10))

Startup Assembly

决定搜索 Startup 类的程序集

Key: startupAssembly

Type: string

Default: 应用程序程序集

Set using: UseStartup

Environment variable: ASPNETCORE_STARTUPASSEMBLY

能够指定程序的名称(string)或者类型 (TStartup)。若是多个 UseStartup 方法被调用,则最后一个优先级最高:

WebHost.CreateDefaultBuilder(args)
    .UseStartup("StartupAssemblyName")
WebHost.CreateDefaultBuilder(args)
    .UseStartup<TStartup>()

Web root

设置应用程序静态资源的相对路径

Key: webroot

Type: string

Default: 默认是 wwwroot。路基 {contentroot}/wwwroot 必须存在。若是路径不存在,一个 no-op 文件提供器将被使用。

WebHost.CreateDefaultBuilder(args)
    .UseWebRoot("public")

更多信息查看:

覆盖配置

使用 Configuration 配置 Web Host。在下面的例子中,host 配置在 hostsetting.json 文件中是可选指定的。任何从 hostsetting.json 文件中加载的配置可能会被命令行参数覆盖。编译的配置(in config)使用 UseConfiguration 来配置主机。IWebHostBuilder 配置被添加到应用程序配置中,可是相反的就不是,ConfigureAppConfigureation 不会影响 IWebHostBuilder 配置。

覆盖 UseUrls 提供的配置优先使用 hostsettings.json 配置,其次是命令行参数配置:

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

    public static IWebHostBuilder CreateWebHostBuilder(string[] args)
    {
        var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("hostsettings.json", optional: true)
            .AddCommandLine(args)
            .Build();

        return WebHost.CreateDefaultBuilder(args)
            .UseUrls("http://*:5000")
            .UseConfiguration(config)
            .Configure(app =>
            {
                app.Run(context => 
                    context.Response.WriteAsync("Hello, World!"));
            });
    }
}

hostsettings.json:

{
    urls: "http://*:5005"
}

注意

UseConfiguration 只会复制 IConfiguration 提供的键值到 host builder 配置。所以,设置 reloadOnChange: true 为 JSON,INI,和 XML 设置文件没有影响。

指定主机在一个特定的 URL 上运行,指望的值能够在运行 dotnet run 时命令行提示中传入。命令行参数覆盖了来自 hostsettings.json 中的 urls 值,服务器在 8080 端口监听:

dotnet run --urls "http://*:8080"

管理 Host

Run

Run 方法启动 web 应用程序并阻塞调用线程直到 Host 关闭:

host.Run();

Start

经过调用它的 Start 方法以 non-blocking 方式运行 Host:

using (host)
{
    host.Start();
    Console.ReadLine();
}

若是一组 URLs 传递给 Start 方法,它就会监听这组指定的 URLs:

var urls = new List<string>()
{
    "http://*:5000",
    "http://localhost:5001"
};

var host = new WebHostBuilder()
    .UseKestrel()
    .UseStartup<Startup>()
    .Start(urls.ToArray());

using (host)
{
    Console.ReadLine();
}

应用程序可使用预设值的默认 CreateDefaultBuilder 使用一个静态约定的方法初始化和启动一个新的 Host。这些方法启动服务器时没有控制台输出,使用 WaitForShutdown 等待终止(Ctrl-C/SIGINT  或者 SIGTERM):

Start(RequestDelegate app)

使用 RequestDelegate 启动:

using (var host = WebHost.Start(app => app.Response.WriteAsync("Hello, World!")))
{
    Console.WriteLine("Use Ctrl-C to shutdown the host...");
    host.WaitForShutdown();
}

在浏览器中发送一个请求 http://localhost:5000 接收到 "Hello World!" 响应,WaitForShutdown 阻塞了直到一个结束信号 (Ctrl-C/SIGINT  或者  SIGTERM) 出现。应用程序显示了 Console.WriteLine 信息,等待按键退出。

Start(string url, RequestDelegate app)

使用一个 URL 和 RequestDelegate 启动应用程序:

using (var host = WebHost.Start("http://localhost:8080", app => app.Response.WriteAsync("Hello, World!")))
{
    Console.WriteLine("Use Ctrl-C to shutdown the host...");
    host.WaitForShutdown();
}

和 Start(RequestDelegate app) 生成一样的结果,指望应用程序在 http://localhost:8080 上响应。

Start(Action <IRouteBuilder> routerBuilder)

使用 IRouteBuilder (Microsoft.AspNetCore.Routing) 的实例使用 routing 中间件:

using (var host = WebHost.Start(router => router
    .MapGet("hello/{name}", (req, res, data) => 
        res.WriteAsync($"Hello, {data.Values["name"]}!"))
    .MapGet("buenosdias/{name}", (req, res, data) => 
        res.WriteAsync($"Buenos dias, {data.Values["name"]}!"))
    .MapGet("throw/{message?}", (req, res, data) => 
        throw new Exception((string)data.Values["message"] ?? "Uh oh!"))
    .MapGet("{greeting}/{name}", (req, res, data) => 
        res.WriteAsync($"{data.Values["greeting"]}, {data.Values["name"]}!"))
    .MapGet("", (req, res, data) => res.WriteAsync("Hello, World!"))))
{
    Console.WriteLine("Use Ctrl-C to shutdown the host...");
    host.WaitForShutdown();
}

对上面的示例使用下面的浏览器请求:

Request Response
http://localhost:5000/hello/Martin Hello,Martin!
http://localhost:5000/buenosdias/Catrina Buenos dias,Catrina!
http://localhost:5000/throw/ooops! Throw an exception with string "ooops!"
http://localhost:5000/throw  Throw an exception with string "Uh oh!"
http://localhost:5000/Sante/Kevin Sante,Kevin! 
 http://localhost:5000 Hello World! 

 WaitForShutdown 阻塞直到结束信号(Ctrl-C/SIGINT 或者 SIGTERM)出现。应用程序显示 Console.WriteLine 信息,等待按键按下退出。

Start(string url, Action<IRouteBuilder> routeBuilder)

使用 URL 和 IRouterBuilder 实例:

using (var host = WebHost.Start("http://localhost:8080", router => router
    .MapGet("hello/{name}", (req, res, data) => 
        res.WriteAsync($"Hello, {data.Values["name"]}!"))
    .MapGet("buenosdias/{name}", (req, res, data) => 
        res.WriteAsync($"Buenos dias, {data.Values["name"]}!"))
    .MapGet("throw/{message?}", (req, res, data) => 
        throw new Exception((string)data.Values["message"] ?? "Uh oh!"))
    .MapGet("{greeting}/{name}", (req, res, data) => 
        res.WriteAsync($"{data.Values["greeting"]}, {data.Values["name"]}!"))
    .MapGet("", (req, res, data) => res.WriteAsync("Hello, World!"))))
{
    Console.WriteLine("Use Ctrl-C to shut down the host...");
    host.WaitForShutdown();
}

生成和 Start(Action<IRouteBuilder> routeBuilder) 相同的结果,指望应用程序在 http://localhost:8080 上响应。

StartWith(Action<IApplicationBuilder> app)

提供一个代理配置一个 IApplicationBuilder:

using (var host = WebHost.StartWith(app => 
    app.Use(next => 
    {
        return async context => 
        {
            await context.Response.WriteAsync("Hello World!");
        };
    })))
{
    Console.WriteLine("Use Ctrl-C to shut down the host...");
    host.WaitForShutdown();
}

在浏览器中请求 http://localhost:5000 接收到 "Hello World!" 响应,WaitForShutdown 阻塞直到一个结束信号(Ctrl-C/SIGINT 或者 SIGTERM)发出。应用程序显示了 Console.WriteLine 信息,等待按键按下退出。

StartWith(string url, Action<IApplicationBuilder> app)

提供一个 url 和一个代理配置 IApplicationBuilder:

using (var host = WebHost.StartWith("http://localhost:8080", app => 
    app.Use(next => 
    {
        return async context => 
        {
            await context.Response.WriteAsync("Hello World!");
        };
    })))
{
    Console.WriteLine("Use Ctrl-C to shut down the host...");
    host.WaitForShutdown();
}

结果和 StartWith(Action<IApplicatonBuilder> app) 相同的结果,指望在应用程序在 http://localhost:8080 上响应。

IWebHostEnvironment interface

IWebHostEnvironment 接口提供了关于应用程序 web 托管环境的信息。使用 constructor injection 访问 IWebHostEnvironment 保证使用它的属性和扩展方法:

public class CustomFileReader
{
    private readonly IWebHostEnvironment _env;

    public CustomFileReader(IWebHostEnvironment env)
    {
        _env = env;
    }

    public string ReadFile(string filePath)
    {
        var fileProvider = _env.WebRootFileProvider;
        // Process the file here
    }
}

一个 convention-based approach 能够基于环境在启动时用来配置应用程序。或者,在 ConfigureServices 中将 IWebHostEnvironment 注入到 Startup 构造方法使用:

public class Startup
{
    public Startup(IWebHostEnvironment env)
    {
        HostingEnvironment = env;
    }

    public IWebHostEnvironment HostingEnvironment { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        if (HostingEnvironment.IsDevelopment())
        {
            // Development configuration
        }
        else
        {
            // Staging/Production configuration
        }

        var contentRootPath = HostingEnvironment.ContentRootPath;
    }
}

注意

除了 IsDevelopment 扩展方法外,IWebHostEnvironment 提供了 IsStaging,IsProduction 和 IsEnvironment(string environment) 方法。更多信息查看,Use multiple environments in ASP.NET Core

 IWebHostEnvironment 服务也能够直接注入到 Configure 方法用来设置处理管道:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        // In Development, use the Developer Exception Page
        app.UseDeveloperExceptionPage();
    }
    else
    {
        // In Staging/Production, route exceptions to /error
        app.UseExceptionHandler("/error");
    }

    var contentRootPath = env.ContentRootPath;
}

当建立自定义中间件(middleware)的时候,IWebHostEnvironment 能够被注入到 Invoke 方法:

IHostApplicationLifetime interface

IHostApplicationLifetime 容许 post-startup 和 关闭。接口的三个属性是取消令牌,用来注册定义启动和关闭时间的Action 方法。 

Cancellation Token Triggered when...
ApplicationStarted 主机已经彻底启动
ApplicationStopped 主机已经彻底正常关闭。全部的请求应该已经处理完毕。关闭阻塞直到这个事件完成。
ApplicationStopping 主机正在正常关闭。请求可能仍然正在处理。关闭阻塞直到这个事件完成。
public class Startup
{
    public void Configure(IApplicationBuilder app, IHostApplicationLifetime appLifetime)
    {
        appLifetime.ApplicationStarted.Register(OnStarted);
        appLifetime.ApplicationStopping.Register(OnStopping);
        appLifetime.ApplicationStopped.Register(OnStopped);

        Console.CancelKeyPress += (sender, eventArgs) =>
        {
            appLifetime.StopApplication();
            // Don't terminate the process immediately, wait for the Main thread to exit gracefully.
            eventArgs.Cancel = true;
        };
    }

    private void OnStarted()
    {
        // Perform post-startup activities here
    }

    private void OnStopping()
    {
        // Perform on-stopping activities here
    }

    private void OnStopped()
    {
        // Perform post-stopped activities here
    }
}

StopApplication 请求结束应用程序。下面的类使用 StopApplication 正常关闭应用程序当类的 Shutdown 方法被调用的时候:

public class MyClass
{
    private readonly IHostApplicationLifetime _appLifetime;

    public MyClass(IHostApplicationLifetime appLifetime)
    {
        _appLifetime = appLifetime;
    }

    public void Shutdown()
    {
        _appLifetime.StopApplication();
    }
}

 

Scope validation

当应用程序的环境是 Development 的时候,CreateDefaultBuilder 设置 ServiceProviderOptions.ValidateScopes 为 true。

当 ValidateScopes 被设置为 true 时,默认的服务提供器执行检查验证:

  • Scoped 服务不能直接或者间接的从根服务提供器中解析出来
  • Scoped 服务不能直接或者间接的注入到单例中

根服务提供器在 BuildServiceProvider 被调用时建立。根服务提供器的生命周期和应用程序/服务器的生命周期一致,当提供器和应用程序一块儿启动,在应用程序关闭时释放。

Scoped 服务由建立它的容器释放。若是一个 scoped 服务在根容器中建立,服务的生命周期会有效的提高为单例,由于它只有在应用程/服务器关闭的时候会释放掉。在调用 BuildServiceProvider 的时候,验证服务会捕捉这些状况。

为了在 Production 环境中老是包含 validate scopes,在 Host builer 上使用 UseDefaultServiceProvider 配置 ServiceProviderOptions

WebHost.CreateDefaultBuilder(args)
    .UseDefaultServiceProvider((context, options) => {
        options.ValidateScopes = true;
    })