前言
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的在线讨论。
如果你感觉对你有帮助,麻烦推荐一下。