• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

ASP.NetCore解读通用主机和托管服务

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

在业务场景中经常需要后台服务不停的或定时处理一些任务,在 asp.net中会使用windows服务来处理,在 asp.net core中可以使用托管服务来实现,托管服务是一个类,具有实现IHostService接口的后台任务逻辑。托管服务必须实现IHostedService接口。抽象类BackgroundService是IHostedService的派生类,实现了IHostService接口定义的方法,因此自定义的托管服务类也可以继承BackgroundService实现ExecuteAsync()方法即可。

1、IHostService

直接上IHostService的定义。托管服务必须实现IHostedService接口:

public interface IHostedService
{
    Task StartAsync(CancellationToken cancellationToken);
    Task StopAsync(CancellationToken cancellationToken);
}
  • StartAsync:当应用程序主机准备好启动服务时触发。

  • StartAsync:当应用程序主机执行正常关闭时触发。

2、使用IHostedService自定义托管服务

自定义托管服务类直接继承IHostedService和IDisposable接口。定时后台任务使用 System.Threading.Timer 类。计时器触发任务的 DoWork 方法。 在 StopAsync 上禁用计时器,并在 Dispose 上处置服务容器时处置计时器:

public class TimedHostedService : IHostedService, IDisposable
    {
        private int executionCount = 0;
        private Timer _timer;

        public Task StartAsync(CancellationToken stoppingToken)
        {
            Console.WriteLine("Timed Hosted Service running.");

            _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));

            return Task.CompletedTask;
        }

        private void DoWork(object state)
        {
            var count = Interlocked.Increment(ref executionCount);

            Console.WriteLine("Timed Hosted Service is working. Count: {Count}", count);
        }

        public Task StopAsync(CancellationToken stoppingToken)
        {
            Console.WriteLine("Timed Hosted Service is stopping.");

            _timer?.Change(Timeout.Infinite, 0);

            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }
    }

使用 AddHostedService 扩展方法在 IHostBuilder.ConfigureServices (Program.cs) 中注册该服务:

static void Main(string[] args)
{
  try
  {
    var host = new HostBuilder().ConfigureServices((hostContext, services) =>
    {
      services.AddHostedService<TimedHostedService>();
    }).Build();

    host.Run();

    Console.WriteLine("Hello World!");
    Console.ReadLine();
  }
  catch (Exception ex)
  {
    Console.WriteLine(ex.Message);
  }
}

3、使用BackgroundService 实现自定义托管服务

由于抽象类BackgroundService已经实现了IHostService接口定义的方法,只需要写子类去继承BackgroundService, 在自己的自定义托管服务类中实现ExecuteAsync()方法

public class MyBackGroundService : BackgroundService
    {
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                Console.WriteLine("MyBackGroundService doing");

                //延迟500毫秒执行 相当于使用了定时器
                await Task.Delay(500, stoppingToken);
            }
        }
    }

使用 AddHostedService 扩展方法在 IHostBuilder.ConfigureServices (Program.cs) 中注册该服务:

static void Main(string[] args)
{
  try
  {
    var host = new HostBuilder().ConfigureServices((hostContext, services) =>
    {
      services.AddHostedService<MyBackGroundService>();
    }).Build();

    host.Run();

    Console.WriteLine("Hello World!");
    Console.ReadLine();
  }
  catch (Exception ex)
  {
    Console.WriteLine(ex.Message);
  }
}

托管的服务总是会被定义成IHostedService接口的实现类型。

public interface IHostedService
{
    Task StartAsync(CancellationToken cancellationToken);
    Task StopAsync(CancellationToken cancellationToken);
}

该接口仅定义了两个用来启动和关闭自身服务的方法。当作为宿主的IHost对象被启动的时候,它会利用依赖注入框架激活每个注册的IHostedService服务,并通过调用StartAsync方法来启动它们。当服务关闭的时候,作为服务宿主的IHost对象会被关闭,由它承载的每个IHostedService服务对象的StopAsync方法也随之被调用。

4、通用主机和托管服务的关系

(1)通用主机是什么?

就是IHostBuilder/IHost为核心的体系。下面会详细介绍。

(2)为什么需要通用主机?

  上边已经介绍过,有些后台服务需要不停的或定时处理一些任务,在 asp.net中会使用windows服务来处理,在 asp.net core中可以使用托管服务来实现,Windows服务也好,托管服务也好,都不过是一个类,那么如何将这些服务类托管起来,运行起来呢?ASP.NET Core提供了以IHostBuilder/IHost为核心的体系用来寄宿托管服务,IHostBuilder/IHost位于.NET Runtime 而不是利用通用主机可以将任意一个或者多个长时间运行的服务寄宿于托管进程中。任何需要在后台长时间运行的操作都可以定义成标准化的服务并利用通用主机来寄宿。

  那么,一个ASP.NET Core应用本质上是一个需要长时间运行的服务,开启这个服务是为了启动一个网络监听器,当监听到抵达的HTTP请求之后,该监听器会将请求传递给应用提供的管道进行处理,管道完成了对请求处理之后会生成HTTP响应,并通过监听器返回客户端。从asp.net core源码中看到GenericWebHostService就是一个托管服务,用来实现一系列的工作,为了让GenericWebHostService运行起来,ASP.NET Core提供了以IHostBuilder/IHost为核心的体系。

  反过来理解也可以,Asp.net Core的Runtime中构建了以IHostBuilder/IHost为核心的体系,目的是能够跨平台,能够与不同的操作系统打交道,是Asp.net Core应用的基础。一个Asp.Net Core应用的一些核心工作交给托管服务处理,托管服务依赖IHostBuilder/IHost为核心的体系。关系图如下:

二、通用主机模型

ASP.NET Core应用程序拥有一个内置的Self-Hosted(自托管)Web Server(Web服务器),用来处理外部请求。不管是托管还是自托管,都离不开Host(宿主),即通用主机。在ASP.NET Core应用中通过配置并启动一个Host来完成应用程序的启动和其生命周期的管理。而Host的主要的职责就是Web Server的配置和Pilpeline(请求处理管道)的构建。

通用主机也叫泛型主机,为什么通用?是因为能够与不同的操作系统交互。为什么能够和不同的操作系统交互?笼统来说,通用主机能够适配不同类型的Http服务器,其中kestrel服务器就是跨平台的关键。具体可以参考NetCore解读服务器

通用主机主要由三个核心对象组成:多个通过IHostedService接口表示的服务被寄宿于通过IHost接口表示的宿主上,IHostBuilder接口表示IHost对象的构建者。 

//定义托管服务GenericWebHostService
internal class GenericWebHostService : IHostedService{}

 

//GenericWebHostService 托管服务承载于Ihost宿主。

_hostBuilder.ConfigureServices((context, services) => services.AddHostedService<GenericWebHostService>()).Build().Run();

在ASP.NET Core应用中通过配置并启动一个Host来完成应用程序的启动和其生命周期的管理,大概流程如下图:

 

 类Host调用CreateDefaultBuilder方法创建HostBuilder实例,HostBuilder实例调用相应的方法和扩展方法完成基础配置后,调用Build()方法创建IHost实例,IHost实例再次调用Run()方法完成通用主机的启动。下面将会详细介绍通用主机模型的实现。

三、IHost

先来介绍下IHost,IHost位于.NET Runtime 而不是通过IHostedService接口表示的托管服务GenericWebHostService 最终被承载于通过IHost接口表示的宿主上。一般来说,aspnetcore应用整个生命周期内只会创建一个IHost对象,启动和关闭应用程序本质上就是启动和关闭作为宿主的IHost对象

public interface IHost : IDisposable
{
    IServiceProvider Services { get; }
    Task StartAsync(CancellationToken cancellationToken = default);
    Task StopAsync(CancellationToken cancellationToken = default);
}

IHost接口派生于IDisposable接口,所以当它在关闭之后,应用程序还会调用其Dispose方法作一些额外的资源释放工作。IHost接口的Services属性返回作为依赖注入容器的IServiceProvider对象,该对象提供了服务承载过程中所需的服务实例,其中就包括需要承载的IHostedService服务。定义在IHost接口中的StartAsync和StopAsync方法完成了针对服务宿主的启动和关闭。

。总而言之,Host是封装应用程序资源的对象,为它所承载的应用提供环境和基本的功能,封装的内容主要包括:

  • DI容器
  • 日志
  • 配置
  • IHostedService实现,启动HTTP服务器的实现的服务就是一个IHostedService(GenericWebHostService)

1、Run方法

IHost会调用Run()方法来启动的,IHost接口中并没有提供Run()方法,其实Run()方法是IHost一个扩展方法,正常情况下,在利用HostBuilder对象构建出IHost对象之后,我们并没有调用其StartAsync方法启动它,而是另一个名为Run的扩展方法。Run方法涉及到服务承载应用生命周期的管理,如果我们调用IHost对象的扩展方法Run,它会在内部调用StartAsync方法,接下来它会持续等待下去直到接收到应用被关闭的通知。当IHost对象利用IHostApplicationLifetime服务接收到关于应用关闭的通知后,它会调用自身的StopAsync方法,针对Run方法的调用此时才会返回。启动IHost对象直到应用关闭的实现体现在WaitForShutdownAsync扩展方法上。具体源码如下:
/// <summary>
/// Runs an application and returns a Task that only completes when the token is triggered or shutdown is triggered.
/// </summary>
/// <param name="host">The <see cref="IHost"/> to run.</param>
/// <param name="token">The token to trigger shutdown.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public static async Task RunAsync(this IHost host, CancellationToken token = default)
{
    try
    {
        await host.StartAsync(token);

        await host.WaitForShutdownAsync(token);
    }
    finally
    {
        if (host is IAsyncDisposable asyncDisposable)
        {
            await asyncDisposable.DisposeAsync();
        }
        else
        {
            host.Dispose();
        }

    }
}

/// <summary>
/// Returns a Task that completes when shutdown is triggered via the given token.
/// </summary>
/// <param name="host">The running <see cref="IHost"/>.</param>
/// <param name="token">The token to trigger shutdown.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public static async Task WaitForShutdownAsync(this IHost host, CancellationToken token = default)
{
    var applicationLifetime = host.Services.GetService<IHostApplicationLifetime>();

    token.Register(state =>
    {
        ((IHostApplicationLifetime)state).StopApplication();
    },
    applicationLifetime);

    var waitForStop = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
    applicationLifetime.ApplicationStopping.Register(obj =>
    {
        var tcs = (TaskCompletionSource<object>)obj;
        tcs.TrySetResult(null);
    }, waitForStop);

    await waitForStop.Task;

    // Host will use its default ShutdownTimeout if none is specified.
    await host.StopAsync();
}

IHost是由 IHostBuilder.Build() 方法创建的,所以我们通过对 IHostBuilder 的配置来实现对 Host 的配置。下面会介绍到IHostBuilder 。

四、类Host

此Host非IHost中的Host,此处的Host是一个类,提供了创建HostBuilder实例的方法,具体看下源码:

namespace Microsoft.Extensions.Hosting
{
    /// <summary>
    /// Provides convenience methods for creating instances of <see cref="IHostBuilder"/> with pre-configured defaults.
    /// </summary>
    public static class Host
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="HostBuilder"/> class with pre-configured defaults.
        /// </summary>
        /// <remarks>
        ///   The following defaults are applied to the returned <see cref="HostBuilder"/>:
        ///   <list type="bullet">
        ///     <item><description>set the <see cref="IHostEnvironment.ContentRootPath"/> to the result of <see cref="Directory.GetCurrentDirectory()"/></description></item>
        ///     <item><description>load host <see cref="IConfiguration"/> from "DOTNET_" prefixed environment variables</description></item>
        ///     <item><description>load app <see cref="IConfiguration"/> from 'appsettings.json' and 'appsettings.[<see cref="IHostEnvironment.EnvironmentName"/>].json'</description></item>
        ///     <item><description>load app <see cref="IConfiguration"/> from User Secrets when <see cref="IHostEnvironment.EnvironmentName"/> is 'Development' using the entry assembly</description></item>
        ///     <item><description>load app <see cref="IConfiguration"/> from environment variables</description></item>
        ///     <item><description>configure the <see cref="ILoggerFactory"/> to log to the console, debug, and event source output</description></item>
        ///     <item><description>enables scope validation on the dependency injection container when <see cref="IHostEnvironment.EnvironmentName"/> is 'Development'</description></item>
        ///   </list>
        /// </remarks>
        /// <returns>The initialized <see cref="IHostBuilder"/>.</returns>
        public static IHostBuilder CreateDefaultBuilder() =>
            CreateDefaultBuilder(args: null);

        /// <summary>
        /// Initializes a new instance of the <see cref="HostBuilder"/> class with pre-configured defaults.
        /// </summary>
        /// <remarks>
        ///   The following defaults are applied to the returned <see cref="HostBuilder"/>:
        ///   <list type="bullet">
        ///     <item><description>set the <see cref="IHostEnvironment.ContentRootPath"/> to the result of <see cref="Directory.GetCurrentDirectory()"/></description></item>
        ///     <item><description>load host <see cref="IConfiguration"/> from "DOTNET_" prefixed environment variables</description></item>
        ///     <item><description>load host <see cref="IConfiguration"/> from supplied command line args</description></item>
        ///     <item><description>load app <see cref="IConfiguration"/> from 'appsettings.json' and 'appsettings.[<see cref="IHostEnvironment.EnvironmentName"/>].json'</description></item>
        ///     <item><description>load app <see cref="IConfiguration"/> from User Secrets when <see cref="IHostEnvironment.EnvironmentName"/> is 'Development' using the entry assembly</description></item>
        ///     <item><description>load app <see cref="IConfiguration"/> from environment variables</description></item>
        ///     <item><description>load app <see cref="IConfiguration"/> from supplied command line args</description></item>
        ///     <item><description>configure the <see cref="ILoggerFactory"/> to log to the console, debug, and event source output</description></item>
        ///     <item><description>enables scope validation on the dependency injection container when <see cref="IHostEnvironment.EnvironmentName"/> is 'Development'</description></item>
        ///   </list>
        /// </remarks>
        /// <param name="args">The command line args.</param>
        /// <returns>The initialized <see cref="IHostBuilder"/>.</returns>
        public static IHostBuilder CreateDefaultBuilder(string[] args)
        {
            var builder = new HostBuilder();

            builder.UseContentRoot(Directory.GetCurrentDirectory());
            builder.ConfigureHostConfiguration(config =>
            {
                config.AddEnvironmentVariables(prefix: "DOTNET_");
                if (args != null)
                {
                    config.AddCommandLine(args);
                }
            });

            builder.ConfigureAppConfiguration((hostingContext, config) =>
            {
                var env = hostingContext.HostingEnvironment;

                config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                      .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);

                if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
                {
                    var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
                    if (appAssembly != null)
                    {
                        config.AddUserSecrets(appAssembly, optional: true);
                    }
                }

                config.AddEnvironmentVariables();

                if (args != null)
                {
                    config.AddCommandLine(args);
                }
            })
            .ConfigureLogging((hostingContext, logging) =>
            {
                var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

                // IMPORTANT: This needs to be added *before* configuration is loaded, this lets
                // the defaults be overridden by the configuration.
                if (isWindows)
                {
                    // Default the EventLogLoggerProvider to warning or above
                    logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
                }

                logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                logging.AddConsole();
                logging.AddDebug();
                logging.AddEventSourceLogger();

                if (isWindows)
                {
                    // Add the EventLogLoggerProvider on windows machines
                    logging.AddEventLog();
                }
            })
            .UseDefaultServiceProvider((context, options) =>
            {
                var isDevelopment = context.HostingEnvironment.IsDevelopment();
                options.ValidateScopes = isDevelopment;
                options.ValidateOnBuild = isDevelopment;
            });

            return builder;
        }
    }
}

类Host提供了两个重载方法,用于生成HostBuilder实例。下面将详细介绍CreateDefaultBuilder方法。

五、IHostBuilder

通用主机的配置基本上都是在IHostBuilder中实现的,IHostBuilder位于.NET Runtime 而不是顾名思义,IHostBuilder是用来构建IHost宿主的,IHost对象在应用启动过程中采用Builder模式由对应的IHostBuilder对象来创建,HostBuilder类型是对IHostBuilder接口的默认实现。通过下面方法创建一个HostBuilder对象并进行相应的配置:

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

1、Host.CreateDefaultBuilder(string[] args)

public static IHostBuilder CreateDefaultBuilder(string[] args);//返回IHostBuilder

Host.CreateDefaultBuilder()用于创建HostBuilder实例,并使用预配置默认值初始化 HostBuilder 类的新实例。以下默认值将应用于返回的 HostBuilder :

  • 将设置 内容根目录ContentRootPath 的结果为GetCurrentDirectory()

  • 从提供的命令行参数加载主机

  • 载入 appsettings.json文件和appsettings.[enviromentName].json文件的配置

  • 开发模式下,保存项目程序集到用户机密

  • 从环境变量中加载应用

  • 从提供的命令行参数加载应用

  • 配置 ILoggerFactory 以记录到控制台、调试和事件源输出

  • 开发模式下,在DI容器上启用作用域验证 EnvironmentName

具体实现看下Host.CreateDefaultBuilder()源码:https://github.com/dotnet/extensions/blob/release/3.1/src/Hosting/Hosting/src/Host.cs

/// <summary>
/// Initializes a new instance of the <see cref="HostBuilder"/> class with pre-configured defaults.
/// </summary>
/// <remarks>
///   The following defaults are applied to the returned <see cref="HostBuilder"/>:
///   <list type="bullet">
///     <item><description>set the <see cref="IHostEnvironment.ContentRootPath"/> to the result of <see cref="Directory.GetCurrentDirectory()"/></description></item>
///     <item><description>load host <see cref="IConfiguration"/> from "DOTNET_" prefixed environment variables</description></item>
///     <item><description>load host <see cref="IConfiguration"/> from supplied command line args</description></item>
///     <item><description>load app <see cref="IConfiguration"/> from 'appsettings.json' and 'appsettings.[<see cref="IHostEnvironment.EnvironmentName"/>].json'</description></item>
///     <item><description>load app <see cref="IConfiguration"/> from User Secrets when <see cref="IHostEnvironment.EnvironmentName"/> is 'Development' using the entry assembly</description></item>
///     <item><description>load app <see cref="IConfiguration"/> from environment variables</description></item>
///     <item><description>load app <see cref="IConfiguration"/> from supplied command line args</description></item>
///     <item><description>configure the <see cref="ILoggerFactory"/> to log to the console, debug, and event source output</description></item>
///     <item><description>enables scope validation on the dependency injection container when <see cref="IHostEnvironment.EnvironmentName"/> is 'Development'</description></item>
///   </list>
/// </remarks>
/// <param name="args">The command line args.</param>
/// <returns>The initialized <see cref="IHostBuilder"/>.</returns>
public static IHostBuilder CreateDefaultBuilder(string[] args)
{
    var builder = new HostBuilder();
    //将设置 内容根目录ContentRootPath 的结果为GetCurrentDirectory()
    builder.UseContentRoot(Directory.GetCurrentDirectory());
    //从提供的命令行参数加载主机
    builder.ConfigureHostConfiguration(config =>
    {
        config.AddEnvironmentVariables(prefix: "DOTNET_");
        if (args != null)
        {
            config.AddCommandLine(args);
        }
    });
    
    builder.ConfigureAppConfiguration((hostingContext, config) =>
    {
        var env = hostingContext.HostingEnvironment;
        //载入 appsettings.json文件和appsettings.[enviromentName].json文件的配置
        config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
              .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
        //开发模式下,保存项目程序集到用户机密
        if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
        {
            var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
            if (appAssembly != null)
            {
                config.AddUserSecrets(appAssembly, optional: true);
            }
        }
        //从环境变量中加载应用
        config.AddEnvironmentVariables();
        //从提供的命令行参数加载应用
        if (args != null)
        {
            config.AddCommandLine(args);
        }
    })
    //配置 ILoggerFactory 以记录到控制台、调试和事件源输出
    .ConfigureLogging((hostingContext, logging) =>
    {
        var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

                // IMPORTANT: This needs to be added *before* configuration is loaded, this lets
                // the defaults be overridden by the configuration.
                if (isWindows)
        {
                    // Default the EventLogLoggerProvider to warning or above
                    logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
        }

        logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
        logging.AddConsole();
        logging.AddDebug();
        logging.AddEventSourceLogger();

        if (isWindows)
        {
                    // Add the EventLogLoggerProvider on windows machines
                    logging.AddEventLog();
        }
    })
    //开发模式下,在DI容器上启用作用域验证 EnvironmentName
    .UseDefaultServiceProvider((context, options) =>
    {
        var isDevelopment = context.HostingEnvironment.IsDevelopment();
        options.ValidateScopes = isDevelopment;
        options.ValidateOnBuild = isDevelopment;
    });

    return builder;
}

HostBuilder创建完成后,下面先来看下HostBuilder的一些扩展方法

2、IHostBuilder方法和扩展方法

Host.CreateDefaultBuilder(args)返回了HostBuilder实例,并做了一些默认初始化的配置。IHostBuilder也提供了一些方法和扩展方法用来进一步的配置,IHostBuilder源码如下:

namespace Microsoft.Extensions.Hosting
{
    public interface IHostBuilder
    {
        IDictionary<object, object> Properties { get; }
        IHost Build();
        IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate);
        IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate);
        IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate);
        IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate);
        IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory);
        IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory);
    }
}

类HostBuilder实现了IHostBuilder,具体实现可以参考源码:https://github.com/dotnet/extensions/blob/release/3.1/src/Hosting/Hosting/src/HostBuilder.cs

  • ConfigureAppConfiguration:载入 appsettings.json文件和appsettings.[enviromentName].json文件的配置。可多次调用,累加结果。
  • ConfigureContainer:配置DI容器,可多次调用,累加结果。
  • ConfigureHostConfiguration:可多次进行调用,并累加结果。
  • ConfigureServices:为Host的容器添加服务,可多次调用,并累加结果。
  • UseServiceProviderFactory:设置用于创建服务提供者的工厂

IHostBuilder同时提供了一些扩展方法,以下是部分扩展方法,扩展方法存在于类HostingHostBuilderExtensions中具体实现源码可以参考:https://github.com/dotnet/extensions/blob/release/3.1/src/Hosting/Hosting/src/HostingHostBuilderExtensions.cs

  • ConfigureLogging,该方法提供ILoggingBuilder对象对日志进行配置。
  • ConfigureWebHost,该方法提供IWebHostBuilder对象对
  • UseConsoleLifetime,该方法使Host监听Console的停止事件如:Ctrl+C 或 SIGTERM。
  • UseContentRoot,指定Host的内容根目录。
  • UseDefaultServiceProvider,使用默认的IServiceProvider,并对其进行配置。
  • UseEnvironment,指定Host的环境参数。

3、IHostBuilder.ConfigureWebHostDefaults()

ConfigureWebHostDefaults也是IHostBuilder的扩展方法,这里重点介绍下。上面介绍的配置主要是 Host 的通用配置,这些配置是各类应用所需的基础功能,所以到目前为止我们的 Host 仍然不具备承载 Web 应用的能力,所以IHostBuilder的扩展方法ConfigureWebHostDefaults引入IWebHostBuilder实例,为 Host 添加一个“WebHost”应具备的核心功能,下面是ConfigureWebHostDefaults的默认配置:

  • 使用 Kestrel 作为Web服务器,并载入配置文件中 Kestrel 的相关配置。
  • 配置默认的静态文件目录。
  • 添加HostFiltering中间件。
  • 如果ASPNETCORE_FORWARDEDHEADERS_ENABLED为true则添加ForwardedHeaders中间件
  • 配置为默认使用IIS集成模式。 

具体试下,请参考ConfigureWebHostDefaults源码:

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore;

namespace Microsoft.Extensions.Hosting
{
    /// <summary>
    /// Extension methods for configuring the <see cref="IHostBuilder" />.
    /// </summary>
    public static class GenericHostBuilderExtensions
    {
        /// <summary>
        /// Configures a <see cref="IHostBuilder" /> with defaults for hosting a web app. This should be called
        /// before application specific configuration to avoid it overwriting provided services, configuration sources,
        /// environments, content root, etc.
        /// </summary>
        /// <remarks>
        /// The following defaults are applied to the <see cref="IHostBuilder"/>:
        /// <list type="bullet">
        ///     <item><description>use Kestrel as the web server and configure it using the application's configuration providers</description></item>
        ///     <item><description>configure <see cref="IWebHostEnvironment.WebRootFileProvider"/> to include static web assets from projects referenced by the entry assembly during development</description></item>
        ///     <item><description>adds the HostFiltering middleware</description></item>
        ///     <item><description>adds the ForwardedHeaders middleware if ASPNETCORE_FORWARDEDHEADERS_ENABLED=true,</description></item>
        ///     <item><description>enable IIS integration</description></item>
        ///   </list>
        /// </remarks>
        /// <param name="builder">The <see cref="IHostBuilder" /> instance to configure.</param>
        /// <param name="configure">The configure callback</param>
        /// <returns>A reference to the <paramref name="builder"/> after the operation has completed.</returns>

     //IHostBuilder的扩展方法,用于接入IWebHostBuilder实例 public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure) { if (configure is null) { throw new ArgumentNullException(nameof(configure)); }        //调用IhostBuilder扩展方法ConfigureWebHost,配置Host return builder.ConfigureWebHost(webHostBuilder => {
          //调用方法ConfigureWebDefaults,配置webhost WebHost.ConfigureWebDefaults(webHostBuilder);           configure(webHostBuilder); }); } } }

从上面源码中可以看出,ConfigureWebHostDefaults中调用了ConfigureWebHost和ConfigureWebDefaults两个方法。其中ConfigureWebHost是IHostBuilder的扩展方法,主要是对ASP.NET Core应用进行配置。ConfigureWebDefaults用来对WebHost做默认配置。先来看下ConfigureWebDefaults。

(1)ConfigureWebDefaults

ConfigureWebDefaults是WebHost的默认配置,ConfigureWebDefaults的源码定义如下:

//IwebHostBuilder的基本配置
internal static void ConfigureWebDefaults(IWebHostBuilder builder)
{
    //配置默认的静态文件目录。
    builder.ConfigureAppConfiguration((ctx, cb) =>
    {
        if (ctx.HostingEnvironment.IsDevelopment())
        {
            StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration);
        }
    });
    //使用 Kestrel 作为Web服务器,并载入配置文件中 Kestrel 的相关配置。
    builder.UseKestrel((builderContext, options) =>
    {
        options.Configure(builderContext.Configuration.GetSection("Kestrel"), reloadOnChange: true);
    })
    .ConfigureServices((hostingContext, services) =>
    {
        // Fallback
        services.PostConfigure<HostFilteringOptions>(options =>
        {
            if (options.AllowedHosts == null || options.AllowedHosts.Count == 0)
            {
                        // "AllowedHosts": "localhost;127.0.0.1;[::1]"
                        var hosts = hostingContext.Configuration["AllowedHosts"]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                        // Fall back to "*" to disable.
                        options.AllowedHosts = (hosts?.Length > 0 ? hosts : new[] { "*" });
            }
        });
        //添加HostFiltering中间件。
        services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>(
                    new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration));
        services.AddTransient<IStartupFilter, HostFilteringStartupFilter>();
        //如果ASPNETCORE_FORWARDEDHEADERS_ENABLED为true则添加ForwardedHeaders中间件
        services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter>();
        services.AddTransient<IConfigureOptions<ForwardedHeadersOptions>, ForwardedHeadersOptionsSetup>();

        services.AddRouting();
    })
    //配置为默认使用IIS集成模式。 
    .UseIIS()
    .UseIISIntegration();
}

 从源码中可以看出,IHostBuilder.ConfigureWebHostDefaults()相关的配置都集中在ConfigureWebDefaults方法中:

  • 使用 Kestrel 作为Web服务器,并载入配置文件中 Kestrel 的相关配置。
  • 配置默认的静态文件目录。
  • 添加HostFiltering中间件。
  • 如果ASPNETCORE_FORWARDEDHEADERS_ENABLED为true则添加ForwardedHeaders中间件
  • 配置为默认使用IIS集成模式。 

(2)ConfigureWebHost

ConfigureWebHost东东有点多,不在这里陈述了,具体在下面五、ConfigureWebHost中陈述。

4、IWebHostBuilder 

上边介绍了IHost/IHostBuilder体系的整个流程,其中IHostBuilder.ConfigureWebHostDefaults的参数类型为Action<IWebHostBuilder>,也介绍了IHostBuilder.ConfigureWebHostDefaults方法中对Action<IWebHostBuilder>类型的委托处理过程。那么传入的Action<IWebHostBuilder>都做了什么事情呢?换句话说,通过HostBuilder.ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)方法引入了IWebHostBuilder,默认为 Host 添加一个“WebHost”应具备的核心功能。默认配置完成后我们可以根据IWebHostBuilder的扩展方法来进行自定义的配置了。

  • Configure,指定一个“启动”方法用于配置web应用,提供 IApplicationBuilder 对象用于配置。
  • CaptureStartupErrors,设置是否要捕获启动时的错误。
  • ConfigureKestrel,配置Kestrel的各种选项。
  • ConfigureLogging,使用 ILoggingBuilder 进行 Logging 配置。
  • PreferHostingUrls,指示主机是否应监听 IWebHostBuilder上配置的Url,而不是 IServer上配置的Url。
  • SuppressStatusMessages,设置是否禁止启动状态消息。
  • UseConfiguration,使用指定的配置。
  • UseContentRoot,设置Host内容目录。
  • UseDefaultServiceProvider,使用默认的服务提供程序,并进行配置。
  • UseEnvironment,配置环境参数。
  • UseHttpSys,将Http.sys指定为Host要使用的Web服务器。
  • UseIIS,使用IISHttpServer替换Kestrel,并启用进程内(w3wp.exe 或 iisexpress.exe)模式。
  • UseIISIntegration,使用IIS作为Kestrel的反向代理服务器。
  • UseKestrel,指定Kestrel作为Web服务器。
  • UseServer,为Host指定Web服务器,IServer。
  • UseShutdownTimeout,设置 Host 的关闭超时时间。
  • UseSockets,指定及配置Kestrel 传输使用的 Sockets。
  • UseStartup,指定用于配置Web应用的Startup类型。
  • UseStaticWebAssets,配置 WebRootFileProvider 使用由引用项目和程序包定义的静态Web资产文件。
  • UseUrls,指定Host需要监听的Url。
  • UseWebRoot,指定 Web 服务器使用的 Root 目录。

下面重点介绍IWebHostBuilder的扩展方法UseStartup。

5、Startup类

从上面的描述中,我们知道IWebHostBuilder提供了UseStartup扩展方法,用于配置Web相关的应用,返回IWebHostBuilder类型实例。Startup中默认定义了两个方法ConfigureServices和Configure。

(1)Startup的构造函数

在构造函数中,可以注入以下服务来使用:

  • IWebHostEnvironment

  • IHostEnvironment

  • IConfiguration

代码如下:

public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment,IHostEnvironment hostEnvironment)
{
      Configuration = configuration;
      WebHostEnvironment = webHostEnvironment;
      HostEnvironment1 = hostEnvironment;
}

 public IConfiguration Configuration { get; }
 public IWebHostEnvironment WebHostEnvironment { get; }
 public IHostEnvironment HostEnvironment1 { get; }

(2)ConfigureServices

ConfigureServices是可选方法,并且会在Configure之前被主机调用。在 ConfigureServices 中注册服务,并通过依赖关系注入 (DI) 或 ApplicationServices 在整个应用中使用服务。该方法的作用就是将服务添加到服务容器,使这些服务可以在应用以及Configure方法中被使用。

public virtual void ConfigureServices (Microsoft.Extensions.DependencyInjection.IServiceCollection services);

这里重点提及IServiceCollection,先来看下IServiceCollection的命名空间:

Microsoft.Extensions.DependencyInjection

顾名思义,IServiceCollection和依赖注入有关,内部有DI容器相关的构造,具体原理这里不做详解。下面列下IServiceCollection 中的部分方法和扩展方法:

AddScoped,添加服务,服务实例的生命周期为Scoped。
AddTransient,添加服务,服务实例的生命周期为Transient(每次被使用都创建新对象)。
AddSingleton,添加服务,服务实例的生命周期为单例。

AddMvc,添加所有MVC所需的服务。
AddMvcCore,仅添加核心必要的MVC所需的服务。
AddControllers,添加启用Controller 所需要的服务,不包括View和Pages所需要的服务。
AddControllersWithViews,添加启用 Controller 以及 Razor 页面所需要的服务。
AddRazorPages,添加 Razor Pages 所需要的服务。

AddAntiforgery,添加防止CSRF攻击的服务。
AddAuthentication,添加启用Authentication中间件所需的服务。
AddAuthenticationCore,添加启用Authentication中间件所需的核心服务。
AddAuthorization,添加启用Authorization中间件所需的服务。
AddAuthorizationCore,添加启用Authorization中间件所需的核心服务。
AddAuthorizationPolicyEvaluator,添加 Authorization 策略评估服务。
AddCertificateForwarding,添加CertificateForwarding中间件所需的服务。
AddConnections,添加 http://ASP.NET Core Connection Handlers 所需的服务。
AddCors,添加CORS中间件 所需的服务。
AddDataProtection,添加 http://ASP.NET Core Data Protection 所需的服务。
 
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap