天天看点

ASP.NET Core 源码解读

ASP.NET Core WebApi 项目搭建

提前准备下载

  1. Vs 2019
  2. .Net Core SDK

一、新建一个.NET Core 项目

这里我选用Vs 创建,先选一个文件夹例如

可以通过可视化界面创建,也可以通过命令行创建

我这里选择命令行创建

第一次打开C#时,会自动帮你下载omnisharp和debugger,在线下载安装需要等待安装完成

二、项目介绍

先介绍查看源码的方式F12

VS2019-工具-文本编辑器-C#-高级-勾选支持导航到反编译

目前只开源了.net Core2.X,3.X的暂时还看不到

Program类

首先介绍Main方法入口,0个引用说明是被框架调用

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

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }      
  1. 前期准备WebHostBuild()

CreateHostBuilder创建WebHost,默认配置

Host是一个静态类,里面有一个CreateDefaultBuilder方法,我们去看源码

这里实例化了一个WebHostBuilder类进去看看

private readonly HostingEnvironment _hostingEnvironment;
        private readonly List<Action<WebHostBuilderContext, IServiceCollection>> _configureServicesDelegates;
        private IConfiguration _config;
        private WebHostOptions _options;
        private WebHostBuilderContext _context;
        private bool _webHostBuilt;
        private List<Action<WebHostBuilderContext, IConfigurationBuilder>> _configureAppConfigurationBuilderDelegates;
        public WebHostBuilder()//默认构造函数
        {
            _hostingEnvironment = new HostingEnvironment();
            _configureServicesDelegates = new List<Action<WebHostBuilderContext, IServiceCollection>>();
            _configureAppConfigurationBuilderDelegates = new List<Action<WebHostBuilderContext, IConfigurationBuilder>>();
            _config = new ConfigurationBuilder().AddEnvironmentVariables("ASPNETCORE_").Build();
            if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.EnvironmentKey)))
            {
                UseSetting(WebHostDefaults.EnvironmentKey, Environment.GetEnvironmentVariable("Hosting:Environment") ?? Environment.GetEnvironmentVariable("ASPNET_ENV"));
            }
            if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.ServerUrlsKey)))
            {
                UseSetting(WebHostDefaults.ServerUrlsKey, Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS"));
            }
            _context = new WebHostBuilderContext
            {
                Configuration = _config
            }//      

这里不就是实现依赖注入吗(构造函数实例化对象)

WebHost前期需要准备默认配置,这些配置它需要用到一些参数和一些变量通过注入的方式。

它具体需要配置那些地方呢

1.UseContentRoot

WebHostBuilder webHostBuilder = new WebHostBuilder();
if (string.IsNullOrEmpty(webHostBuilder.GetSetting(WebHostDefaults.ContentRootKey)))
{
    webHostBuilder.UseContentRoot(Directory.GetCurrentDirectory());
}      

指定了Web主机所使用的内容、站点根目录

因为前期就要将那些wwwroot静态文件,静态网页、配置文件读取到,所以先指定目录

2.UseConfiguration

if (args != null)
{
     webHostBuilder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build());
}      

传的控制台命令args就在这里执行

3.UseKestrel

webHostBuilder.UseKestrel(delegate (WebHostBuilderContext builderContext, KestrelServerOptions options)
{
   options.Configure(builderContext.Configuration.GetSection("Kestrel"));
}      

配置Kestrel服务器

4.ConfigureAppConfiguration

ConfigureAppConfiguration(delegate (WebHostBuilderContext hostingContext, IConfigurationBuilder config)
{
    IHostingEnvironment hostingEnvironment = hostingContext.HostingEnvironment;
    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).AddJsonFile("appsettings." + hostingEnvironment.EnvironmentName + ".json", optional: true, reloadOnChange: true);
    if (hostingEnvironment.IsDevelopment())
        {
             Assembly assembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName));
             if (assembly != null)
               {
                config.AddUserSecrets(assembly, optional: true);//在开发环境使用UserSecrets
              }
        }
                config.AddEnvironmentVariables();//自动获取环境变量
                if (args != null)
                {
                    config.AddCommandLine(args);//命令行参数
                }
}
      

  

首先配置了appsettings.json,然后规定在开发环境使用UserSecrets,UserSecrets是个人的密钥,项目中是不会推送至远程仓库,该怎么创建呢

5.ConfigureLogging

ConfigureLogging(delegate (WebHostBuilderContext hostingContext, ILoggingBuilder logging)
            {
                //添加服务的地方Logging配置
                logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                logging.AddConsole();//控制台
                logging.AddDebug();//Debug
                logging.AddEventSourceLogger();//事件
            }      

配置日志

6.ConfigureServices

ConfigureServices(delegate (WebHostBuilderContext hostingContext, IServiceCollection services)
                {
                    services.PostConfigure(delegate (HostFilteringOptions options)
                    {
                        if (options.AllowedHosts == null || options.AllowedHosts.Count == 0)
                        {
                            string[] array = hostingContext.Configuration["AllowedHosts"]?.Split(new char[1]
                            {
                                ';'
                            }, StringSplitOptions.RemoveEmptyEntries);
                            options.AllowedHosts = ((array != null && array.Length != 0) ? array : new string[1]
                            {
                                "*"
                            });
                        }
                    });
                    services.AddSingleton((IOptionsChangeTokenSource<HostFilteringOptions>)new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration));
                    services.AddTransient<IStartupFilter, HostFilteringStartupFilter>();
                }      

ConfigureServices是Startup方法里面的,会自动把我们的一些服务进行默认配置,我们提前写好的配置服务,在WebHostBuilder里面构建,进行服务注入。

最后返回一个WebHostBuilder,就完成构建了。

我们在默认配置的时候是默认不包含我们自己创建的Startup的服务,而是系统默认的服务。

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }      
  1. Build()-构建

构建一个Microsoft.AspNetCore.Hosting,托管Web应用程序的WebHost,一个方法只会执行一次

  1. Run()-构启动

发现run方法下面是一个异步方法

public static void Run(this IWebHost host)
        {
            host.RunAsync().GetAwaiter().GetResult();
        }

        public static async Task RunAsync(this IWebHost host, CancellationToken token = default(CancellationToken))
        {
            if (token.CanBeCanceled)
            {
                await host.RunAsync(token, null);
                return;
            }
            ManualResetEventSlim done = new ManualResetEventSlim(initialState: false);
            using (CancellationTokenSource cts = new CancellationTokenSource())
            {
                AttachCtrlcSigtermShutdown(cts, done, host.Services.GetRequiredService<WebHostOptions>().SuppressStatusMessages ? string.Empty : "Application is shutting down...");
                try
                {
                    await host.RunAsync(cts.Token, "Application started. Press Ctrl+C to shut down.");
                }
                finally
                {
                    done.Set();
                }
            }
        }

        private static async Task RunAsync(this IWebHost host, CancellationToken token, string shutdownMessage)
        {
            using (host)
            {
                await host.StartAsync(token);
                IHostingEnvironment service = host.Services.GetService<IHostingEnvironment>();
                if (!host.Services.GetRequiredService<WebHostOptions>().SuppressStatusMessages)
                {
                    Console.WriteLine("Hosting environment: " + service.EnvironmentName);
                    Console.WriteLine("Content root path: " + service.ContentRootPath);
                    ICollection<string> collection = host.ServerFeatures.Get<IServerAddressesFeature>()?.Addresses;
                    if (collection != null)
                    {
                        foreach (string item in collection)
                        {
                            Console.WriteLine("Now listening on: " + item);
                        }
                    }
                    if (!string.IsNullOrEmpty(shutdownMessage))
                    {
                        Console.WriteLine(shutdownMessage);
                    }
                }
                await host.WaitForTokenShutdownAsync(token);
            }
        }      

因此显示以下信息

三、.Net Core进程内外托管

进程是指IIS Worker Process工作进程

启动IIS运行.net Core项目可以在任务管理器中看到,结束项目运行进程消失

进程内托管就是指的这个

进程外托管就是dotnet.exe,打开网页可以看到服务器

双击项目

AspNetCoreHostingModel配置了托管模式。InProcess指定进程内,OutProcess指定进程外

1.当AspNetCoreHostingModel设置InProcess进程内托管,启动IIS,任务管理器这时候有IIS Worker Process,网页服务器显示IIS

2.当AspNetCoreHostingModel设置InProcess进程内托管,启动项目,任务管理器这时候没有IIS Worker Process,网页服务器显示IIS

3.当AspNetCoreHostingModel设置OutProcess进程外托管,启动IIS,任务管理器这时候还是有IIS Worker Process,网页服务器显示Kestrel

4.当AspNetCoreHostingModel设置OutProcess进程外托管,启动项目,任务管理器没有IIS Worker Process,网页服务器显示Kestrel

这是怎么回事,怎么进程内外都会启动IIS Worker Process,进程内外到底有什么区别

原因分析:蓝色字体第1、3点进程内外的IIS服务都会启动,不过启动的作用确实不一样的

1.进程内启动IIS是当作服务器

2.进程外也启动IIS,是Kestrel配合IIS,将IIS当作反向代理服务器使用,不是作为整个项目的应用服务器了

以前是请求通过IIS监听,通过IIS托管代码响应直接完成

ASP.NET Core不同的是 靠ASP.NET Core Module把HTTP请求转发

IIS这个时候等同于Nginx反向代理,本身不再处理了

ASP.NET Core内置Kestrel服务器等价于IIS--Webserver