.NET CORE学习笔记系列(5)——ASP.NET CORE的运行原理解析

时间:2023-11-26 09:28:02

一、概述

  在ASP.NET Core之前,ASP.NET Framework应用程序由IIS加载。Web应用程序的入口点由InetMgr.exe创建并调用托管,初始化过程中触发HttpApplication.Application_Start()事件。开发人员第一次执行代码的机会是处理Application_StartGlobal.asax中的事件。在ASP.NET Core中,Global.asax文件不再可用,已被新的初始化过程替代。

  .NET CORE学习笔记系列(5)——ASP.NET CORE的运行原理解析

二、宿主

  ASP.NET Core应用程序其本质上是一个独立的控制台应用,是在.NET Core 控制台程序下调用特定的库,它并不是必需在 IIS内部托管且并不需要IIS来启动运行(而这正是ASP.NET Core跨平台的基石)。ASP.NET Core应用程序拥有一个内置的 Self-Hosted(自托管)的Web Server(Kestrel服务器),用来处理外部请求。 不管是托管还是自托管,都离不开Host(宿主)。在ASP.NET Core应用中通过配置并启动一个Host来完成应用程序的启动和其生命周期的管理。 而Host的主要的职责就是Web Server的配置和Pilpeline(请求处理管道)的构建。

  既然提到了宿主,下面就详细解释一下:

什么是宿主?

  ASP.NET Core应用程序需要在宿主中执行,一个宿主必须实现 IWebHost接口,该接口暴露了功能和服务的集合,以及一个 Start方法。宿主通常使用 WebHostBuilder的实例来创建,该实例构建并返回一个 WebHost实例。 WebHost引用一个将用于处理请求的服务器Kestrel。

宿主和服务器有什么不同?

  宿主负责应用程序启动和生命周期的管理。服务器Kestrel负责接受HTTP请求。宿主的部分职责还包括确保应用程序服务和服务器Kestrel可用并正确配置。你可以把宿主看作是服务器Kestrel的包装。宿主引用了Kestrel服务器接收HTTP请求;服务器并不知道它的宿主。

  所有的ASP.NET托管库都是从Program开始执行,而不是由IIS托管。

public class Program
    {    /// 创建宿主的步骤    /// ASP.NET Core应用程序的启动主要包含三个步骤:    /// CreateDefaultBuilder():创建IWebHostBuilder    /// Build():IWebHostBuilder负责创建IWebHost    /// Run():启动IWebHost
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();// 调用Startup.cs类下的Configure 和 ConfigureServices
    }

以上是Program类中Main方法的示例代码,Main方法负责初始化Host宿主,调用Startup和执行应用程序。宿主将调用Startup类下面的ConfigureConfigureServices方法。

三、WebHostBuild

  WebHostBuild 用来构建 WebHost ,也是我们最先接触的一个类,它提供了如下方法:

  1、ConfigureAppConfiguration

  Configuration 在 ASP.NET Core 进行了全新的设计,使其更加灵活简洁,可以支持多种数据源。在 ASP.NET Core 1.x 中,我们是在Startup的构造函数中配置各种数据源的,而在 ASP.NET Core 2.0 中则移动了到Program中,这样能与控制台应用程序保持一致:

public static class WebHostBuilderExtensions
{
    public static IWebHostBuilder ConfigureAppConfiguration(this IWebHostBuilder hostBuilder, Action<IConfigurationBuilder> configureDelegate)
    {
        return hostBuilder.ConfigureAppConfiguration((context, builder) => configureDelegate(builder));
    }
}

public class WebHostBuilder : IWebHostBuilder
{
    private List<Action<WebHostBuilderContext, IConfigurationBuilder>> _configureAppConfigurationBuilderDelegates;
    public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate)
    {
        if (configureDelegate == null)
        {
            throw new ArgumentNullException(nameof(configureDelegate));
        }

        _configureAppConfigurationBuilderDelegates.Add(configureDelegate);
        return this;
    }
}

_configureAppConfigurationBuilderDelegates委托会在 WebHostBuilder 的Build方法中执行,生成 IConfiguration 对象并以单例的形式注册到 DI 系统中, 我们可以在Startup以及应用程序的任何地方,通过 DI 系统来获取到。在CreateDefaultBuilder中会通过该方法来添加appsettinggs.json等基本配置的配置源

   2、UseSetting

  UseSetting 是一个非常重要的方法,它用来配置 WebHost 中的 IConfiguration 对象。需要注意与上面ConfigureAppConfiguration的区别, WebHost 中的 Configuration 只限于在 WebHost 使用,并且我们不能配置它的数据源,它只会读取ASPNETCORE_开头的环境变量:

private IConfiguration _config;

public WebHostBuilder()
{
    _config = new ConfigurationBuilder()
        .AddEnvironmentVariables(prefix: "ASPNETCORE_")
        .Build();
}

而我们比较熟悉的当前执行环境,也是通过该_config来读取的,虽然我们不能配置它的数据源,但是它为我们提供了一个UseSetting方法,为我们提供了一个设置_config的机会:

public string GetSetting(string key)
{
    return _config[key];
}

而我们通过UseSetting设置的变量最终也会以MemoryConfigurationProvider的形式添加到上面介绍的ConfigureAppConfiguration所配置的IConfiguration对象中。

  3、UseStartup

  UseStartup 这个我们都比较熟悉,它用来显式注册我们的Startup类,可以使用泛性,Type , 和程序集名称三种方式来注册:

// 常用的方法
public static IWebHostBuilder UseStartup<TStartup>(this IWebHostBuilder hostBuilder) where TStartup : class
{
    return hostBuilder.UseStartup(typeof(TStartup));
}

// 通过指定的程序集来注册 Startup 类
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, string startupAssemblyName)
{
    if (startupAssemblyName == null)
    {
        throw new ArgumentNullException(nameof(startupAssemblyName));
    }

    return hostBuilder
        .UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
        .UseSetting(WebHostDefaults.StartupAssemblyKey, startupAssemblyName);
}

// 最终的 Startup 类注册方法,上面两种只是一种简写形式
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
{
    ....
}

  4、ConfigureLogging

  ConfigureLogging 用来配置日志系统,在 ASP.NET Core 1.x 中是在Startup类的Configure方法中,通过ILoggerFactory扩展来注册的,在 ASP.NET Core 中也变得更加简洁,并且统一通过 WebHostBuild 来配置:

public static class WebHostBuilderExtensions
{
    public static IWebHostBuilder ConfigureLogging(this IWebHostBuilder hostBuilder, Action<ILoggingBuilder> configureLogging)
    {
        return hostBuilder.ConfigureServices(collection => collection.AddLogging(configureLogging));
    }

    public static IWebHostBuilder ConfigureLogging(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, ILoggingBuilder> configureLogging)
    {
        return hostBuilder.ConfigureServices((context, collection) => collection.AddLogging(builder => configureLogging(context, builder)));
    }
}

AddLogging 是Microsoft.Extensions.Logging提供的扩展方法。

四、 Starup文件配置

  对于一个ASP.NET Core 程序而言,Startup 类是必须的。ASP.NET Core在程序启动时会从Program类中开始执行,然后再找到UseStartup<Startup>中找到配置的Startup的类,如果不指定Startup类会导致启动失败。

  在Startup中必须定义Configure方法,而ConfigureServices方法则是可选的,方法会在程序第一次启动时被调用,类似传统的ASP.NET MVC的路由和应用程序状态均可在Startup中配置,也可以在此初始化所需中间件。

Configure

  在ASP.NET Core 应用程序中Configure方法用于指定中间件以什么样的形式响应HTTP请求。 Configure 方法是用来配置请求管道的,通常会在这里会注册一些中间件

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();//中间件:允许应用程序提供静态资源
            app.UseCookiePolicy();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });//中间件:将MVC添加到管道并允许配置路由
        }

  ASP.NET Core是通过对IApplicationBuilder进行扩展来构建中间件的, 上面代码中每个use扩展方法都是将中间件添加到请求管道。也可以给Configure方法附加服务(如:IHostingEnvironment)这些服务在ConfigureServices方法中被初始化。

用ASP.NET Core项目模板添加的应用程序,默认添加的几个中间件:

  • UseStaticFiles 允许应用程序提供静态资源。

  • UseMvc 将MVC添加到管道并允许配置路由。

ConfigureServices

  ConfigureServices方法是应用程序运行时将服务添加到容器中,用ASP.NET Core项目模板的时候默认会将MVC的服务添加到容器中。

public void ConfigureServices(IServiceCollection services){
    services.AddMvc();
}

五、处理管道(中间件)

  在ASP.NET Core应用程序中使用中间件,应用程序所做的任何事情(包括服务器中的静态文件)都是由中间件来完成的。没有任何中间件的应用程序在请求的出错时候简单返回404 Not Found。中间件可以让您完全控制请求的处理方式,并且让您的应用程序更加精简。

当接收到一个请求时,请求会交给中间件构成的中间件管道进行处理,管道就是多个中间件构成,请求从一个中间件的一端进入,从中间件的另一端出来,每个中间件都可以对HttpContext请求开始和结束进行处理:

参考

运行原理:https://blog.csdn.net/sD7O95O/article/details/78126384

宿主介绍:http://www.jkeabc.com/460935.html

WebHostBuild:https://blog.csdn.net/linux12a/article/details/77676127

中间件:https://www.cnblogs.com/vipyoumay/p/5640645.html

中间件请求管道的构成:https://blog.csdn.net/bieguolaia/article/details/77816445