asp.net core 源码系列(一)

前言

asp.net core 出来一个来月了,自己在工作课余时间准备读一下它的源代码,会把一系列的理解和想法记录下来,同时更希望能够得到园子里同行的指点,共同进步,这里只是我对源代码的理解,有错误地方,望大家指正,这将会是一系列的文章。

入口

创建一个asp.net core项目后可以看到一个Program.cs和一个Startup.cs,今天主要研究一下Program.cs到底都做了些什么,

public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }

这里main方法是应用程序的入口不用多说,可以看到在入口处创建了一个WebHostBuilder的实例对象,然后对他进行了一系列的配置,最后执行Build方法返回一个IWebHost对象的实例,最后执行他的Run,方法,至此,应用程序启动起来了,那么这一系列的配置到底做了什么,我们需要看WebHost这个类,我们去github上面把源代码下载下来慢慢看。链接是aspnet/hosting,下载下来后可以直接用VS2015 update3 打开。首先我们要先来看WebHostBuilder这个类

WebHostBuilder

这个类的实例是为了创建WebHost的类实例而准备的,我们看一下他的构造方法:

public WebHostBuilder()
        {
            _hostingEnvironment = new HostingEnvironment();
            _configureServicesDelegates = new List<Action<IServiceCollection>>();
            _configureLoggingDelegates = new List<Action<ILoggerFactory>>();

            _config = new ConfigurationBuilder()
                .AddEnvironmentVariables(prefix: "ASPNETCORE_")
                .Build();

            if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.EnvironmentKey)))
            {
                // Try adding legacy environment keys, never remove these.
                UseSetting(WebHostDefaults.EnvironmentKey, Environment.GetEnvironmentVariable("Hosting:Environment")
                    ?? Environment.GetEnvironmentVariable("ASPNET_ENV"));
            }

            if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.ServerUrlsKey)))
            {
                // Try adding legacy url key, never remove this.
                UseSetting(WebHostDefaults.ServerUrlsKey, Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS"));
            }
        }

这个方法里面初始化一些基本的变量,_hostingEnvironment(host的环境配置,包括环境名字,应用程序的名字,webroot的路径,ContentRoot的路径),_configureServicesDelegates(从名字上来看是服务操作的委托集合),_configureLoggingDelegates(从名字上看是日志操作的委托集合),_config(配置创建器的一个实例),然后会向_config中写入两个配置(“environment”和“urls”)。

然后调用扩展方法UseKestrel(),我们来看代码:

  public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder)
    {
      return hostBuilder.ConfigureServices((Action<IServiceCollection>) (services =>
      {
        ServiceCollectionServiceExtensions.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>(services);
        ServiceCollectionServiceExtensions.AddSingleton<IServer, KestrelServer>(services);
      }));
    }

这个方法是调用WebHostBuilder的ConfigureServices方法增加了Kestrel对host的一些委托处理方法,这些委托方法主要包括将Kestrel以及它的一些配置选项加入到应用程序的服务处理集合。

然后调用UseContentRoot(Directory.GetCurrentDirectory())方法,我们来看代码:

public static IWebHostBuilder UseContentRoot(this IWebHostBuilder hostBuilder, string contentRoot)
    {
      if (contentRoot == null)
        throw new ArgumentNullException("contentRoot");
      return hostBuilder.UseSetting(WebHostDefaults.ContentRootKey, contentRoot);
    }

这个扩展方法依旧是调用UseSeeting()方法给_config配置中添加配置项“contentRoot”(应用程序的内容目录,详见链接),值为当前的目录.

然后调用UseIISIntegration()这个扩展方法,我们来看代码:

public static IWebHostBuilder UseIISIntegration(this IWebHostBuilder app)
    {
      if (app == null)
        throw new ArgumentNullException("app");
      string str1 = app.GetSetting(WebHostBuilderIISExtensions.ServerPort) ?? Environment.GetEnvironmentVariable(string.Format("ASPNETCORE_{0}", (object) WebHostBuilderIISExtensions.ServerPort));
      string str2 = app.GetSetting(WebHostBuilderIISExtensions.ServerPath) ?? Environment.GetEnvironmentVariable(string.Format("ASPNETCORE_{0}", (object) WebHostBuilderIISExtensions.ServerPath));
      string pairingToken = app.GetSetting(WebHostBuilderIISExtensions.PairingToken) ?? Environment.GetEnvironmentVariable(string.Format("ASPNETCORE_{0}", (object) WebHostBuilderIISExtensions.PairingToken));
      if (!string.IsNullOrEmpty(str1) && !string.IsNullOrEmpty(str2) && !string.IsNullOrEmpty(pairingToken))
      {
        string str3 = "http://localhost:" + str1 + str2;
        app.UseSetting(WebHostDefaults.ServerUrlsKey, str3);
        HostingAbstractionsWebHostBuilderExtensions.CaptureStartupErrors(app, true);
        app.ConfigureServices((Action<IServiceCollection>) (services =>
        {
          ServiceCollectionServiceExtensions.AddSingleton<IStartupFilter>(services, (IStartupFilter) new IISSetupFilter(pairingToken));
          OptionsServiceCollectionExtensions.Configure<ForwardedHeadersOptions>(services, (Action<ForwardedHeadersOptions>) (options =>
          {
            options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
            bool flag = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"));
            options.RequireHeaderSymmetry = !flag;
          }));
        }));
      }
      return app;
    }

这里主要是对IIS的一些配置,我的理解是IIS会把它监听的到的请求转交给Kestrel来做处理,所以真正的监听还是在IIS,当然这一步也可以直接省去,直接有Kestrel来监听端口的请求。注释掉这个方法的调用,启动应用程序直接访问 localhost:5000一样可以(你可以试一试的)。

接着是 UseStartup<Startup>() 这个方法,这个方法和Startup这个类,我会在下一篇做单独的讲解,主要是设置Aspnet core的处理模块(我们这里就是MVC的处理)

最后是Builder()方法:

public IWebHost Build()
        {
            // Warn about deprecated environment variables
            if (Environment.GetEnvironmentVariable("Hosting:Environment") != null)
            {
                Console.WriteLine("The environment variable ‘Hosting:Environment‘ is obsolete and has been replaced with ‘ASPNETCORE_ENVIRONMENT‘");
            }

            if (Environment.GetEnvironmentVariable("ASPNET_ENV") != null)
            {
                Console.WriteLine("The environment variable ‘ASPNET_ENV‘ is obsolete and has been replaced with ‘ASPNETCORE_ENVIRONMENT‘");
            }

            if (Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS") != null)
            {
                Console.WriteLine("The environment variable ‘ASPNETCORE_SERVER.URLS‘ is obsolete and has been replaced with ‘ASPNETCORE_URLS‘");
            }

            var hostingServices = BuildHostingServices();
            var hostingContainer = hostingServices.BuildServiceProvider();

            var host = new WebHost(hostingServices, hostingContainer, _options, _config);

            host.Initialize();

            return host;
        }

这个方法根据之前注册的一系列的服务(监听服务,日志服务等等)和配置,生成一个应用程序的最终宿主host,并且初始化他(里面的调用和创建方法源代码都很清晰)。

然后调用host.run()方法,启动服务,run方法的具体调用会单独一个章节来分析。

总结

这篇文章只是对创建一个host对象做了一些系统的描述,里面的具体实现真的很多很多,有一些我还没有看到,感觉每一个方法都有很多的东西,我会慢慢完善,有理解错误的地方,希望大家指出来。愿.NET的生态圈越来越好,大家一起努力。给大家推荐一个网址链接

里面有关于aspnet的在线讨论。

如果你感觉对你有帮助,麻烦推荐一下。

时间: 2024-10-10 15:15:17

asp.net core 源码系列(一)的相关文章

asp.net core源码飘香:Configuration组件(转)

简介: 这是一个基础组件,是一个统一的配置模型,配置可以来源于配置文件(json文件,xml文件,ini文件),内存对象,命令行参数,系统的环境变量又或者是你自己扩展的配置源,该组件将各个配置源的数据按统一的格式(IDictionary<string, string> Data)进行加载,进而对外提供调用接口. 不仅如此,有些配置源(如文件配置源)还可以在配置源的数据发生变化时进行重新加载(IDictionary<string, string> Data),而程序员随时可以判断是否

asp.net core源码飘香:Logging组件(转)

简介: 作为基础组件,日志组件被其他组件和中间件所使用,它提供了一个统一的编程模型,即不需要知道日志最终记录到哪里去,只需要调用它即可. 使用方法很简单,通过依赖注入ILogFactory(CreateLogger方法)或ILogger<T>对象,获取一个ILogger对象,然后通过ILogger的各种扩展方法(都是调用Log方法)记录不同级别的日志. 源码剖析: 总结: 日志组件其实就是工厂模式的应用,但进行了改进,LoggerFactory每次都返回一个Logger对象,而Logger对象

asp.net core源码地址

https://github.com/dotnet/corefx 这个是.net core的 开源项目地址 https://github.com/aspnet 这个下面是asp.net core 框架的地址,里面有很多仓库. https://github.com/aspnet/EntityFrameworkCore  EF Core源码 https://github.com/aspnet/Configuration 配置模块源码 https://github.com/aspnet/Routing

asp.net core源码飘香:Configuration组件

简介: 这是一个基础组件,是一个统一的配置模型,配置可以来源于配置文件(json文件,xml文件,ini文件),内存对象,命令行参数,系统的环境变量又或者是你自己扩展的配置源,该组件将各个配置源的数据按统一的格式(IDictionary<string, string> Data)进行加载,进而对外提供调用接口. 不仅如此,有些配置源(如文件配置源)还可以在配置源的数据发生变化时进行重新加载(IDictionary<string, string> Data),而程序员随时可以判断是否

asp.net core源码飘香:从Hosting开始

知识点: 1.Kestrel服务器启动并处理Http请求的过程. 2.Startup的作用. 源码飘香: 总结: asp.net core将web开发拆分为多个独立的组件,大多以http中间件的形式添加到请求管线,也有一下作为基础组件(configution,options,logging等)提供. 零零散散的二三十个框架吧,除了mvc(还有razor)和ef core是比较大型的框架,其他小框架都可以一遍博客讲完一个.后面三个大框架准备开单独的系列来细说. 今晚就到此为止吧,如果大家有兴趣,请

ASP.NET Core 源码阅读笔记(5) ---Microsoft.AspNetCore.Routing路由

这篇随笔讲讲路由功能,主要内容在项目Microsoft.AspNetCore.Routing中,可以在GitHub上找到,Routing项目地址. 路由功能是大家都很熟悉的功能,使用起来也十分简单,从使用的角度来说可讲的东西不多.不过阅读源码的过程的是个学习的过程,看看顶尖Coder怎么组织代码也是在提升自己. 我们知道现在ASP.NET Core中所有用到的功能都是服务,那么Routing服务是什么时候被添加到依赖注入容器的呢?答案是在StartUp类的ConfigureServices方法中

ASP.NET Core 源码阅读笔记(3) ---Microsoft.AspNetCore.Hosting

有关Hosting的基础知识 Hosting是一个非常重要,但又很难翻译成中文的概念.翻译成:寄宿,大概能勉强地传达它的意思.我们知道,有一些病毒离开了活体之后就会死亡,我们把那些活体称为病毒的宿主.把这种概念应用到托管程序上来,CLR不能单独存在,它必须依赖于某一个进程,我们把这种状况称之为:CLR必须寄宿于某一个进程中,而那个进程就是宿主. ASP.NET Core的一个大的改变就是就是将Web应用程序改成了自寄宿(当然在Windows上也还支持寄宿在IIS中).什么意思呢?我们知道,在之前

[转载]ASP.NET Core 源码阅读笔记(3) ---Microsoft.AspNetCore.Hosting

有关Hosting的基础知识 Hosting是一个非常重要,但又很难翻译成中文的概念.翻译成:寄宿,大概能勉强地传达它的意思.我们知道,有一些病毒离开了活体之后就会死亡,我们 把那些活体称为病毒的宿主.把这种概念应用到托管程序上来,CLR不能单独存在,它必须依赖于某一个进程,我们把这种状况称之为:CLR必须寄宿于某一个进程中,而那个进程就是宿主. ASP.NET Core的一个大的改变就是就是将Web应用程序改成了自寄宿. 什么意思呢?我们知道,在之前的ASP.NET版本中,ASP.NET的We

[转载]ASP.NET Core 源码阅读笔记(2) ---Microsoft.Extensions.DependencyInjection生命周期管理

在上一篇文章中我们主要分析了ASP.NET Core默认依赖注入容器的存储和解析,这一篇文章主要补充一下上一篇文章忽略的一些细节:有关服务回收的问题,即服务的生命周期问题.有关源码可以去GitHub上找到. 这次的主角就是ServiceProvider一人,所有有关生命周期的源码几乎都集中在ServiceProvider.cs这个文件中. 我们知道服务的生命周期由三种,分别是: Transient Scoped Singleton 首先给出我的结论:这三种生命周期类别本质上没有区别,服务的生命周