ASP.NET Core 启动方式(Hosting)
之前版本的ASP.NET程序必須依賴IIS來啟動,而IIS上會為掛載在其中的ASP.NET 注冊一個ISAPI filter。每當http請求過來時,IIS則會啟動w3wp的worker process來開始整個ASP.NET runtime程序。相信大家都這樣的流程都有相應的了解。在.net core之前,ASP.NET的主要場景都是運行在Windows平臺的,IIS也就是web server的首選了。雖然也有類似于jexus的linux webserver可用,但是基于mono的.NET 總體還是不夠Microsoft 原生的強。
不過到了現在,一切都不同了。
新版ASP.NET Core有了.NET Core的支援后已經開始了它的跨平臺之旅了,因此ASP.NET Core的啟動方式也得開始重新設計以適應新需求了。
1、Kestrel 和 IIS platform handler
在 ASP.NET Core 中,整個runtime都是重寫過的,所以它和IIS之間的關系也有所改變。而ASP.NET Core為了跨平臺,它現在的執行方式就如一般的Console app一樣。ASP.NET Core自帶一個高性能的I/O組件 - Kestrel,使得它可以不依賴IIS的存在便啟動了runtime。不過Kestrel 也只是一個I/O組件,并沒有想IIS提供其它的功能來保護和管理ASP.NET 應用程序。ASP.NET Core同樣可以通過IIS進行處理。但是如果通過IIS來進行處理的話,這個時候我們便需要一個“中間人”的角色來負責這個功能了。這個“中間人”的名字叫 Http Platform Handler,主要表現在web.config文檔中的設置,其中包括啟動ASP.NET Core 程序的的路徑和名稱,需要傳入的參數以及一些其他的設置選項。Http Platform Handler的具體設置例子如下:
| <system.webServer> <handlers> <add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/> </handlers> <httpPlatform processPath="WebApp.exe" arguments="" stdoutLogEnabled="false" startupTimeLimit="3600"/> </system.webServer> | 
關于Http Platform Handler的相關資料可以看這個鏈接:
http://www.iis.net/downloads/microsoft/httpplatformhandler
從上面的例子可以看出來,ASP.NET Core編譯之后便是一個EXE程序,使得你可以直接運行。因此,當HTTP請求進來時,IIS先接受請求,然后根據你設置的web.config的內容將請求轉發給WebApp.exe(你的ASP.NET Core程序),然后WebApp.exe開始執行時便會啟動Kestrel,接著這個HTTP請求便進入了ASP.NET Core runtime的世界。這樣看來,IIS這時候只是一個簡單的proxy/forwarder角色。
PS:在我翻譯整理這個文章的時候,世界已經發生了變化:下個版本的asp.net core 將會有全新的IIS module來取代Http Platform Handler。
具體詳細資料見:https://github.com/aspnet/IISIntegration/issues/105
2、Main()
ASP.NET Core和其他的.NET 程序一樣擁有一個static void Main(),這是整個runtime的進入點。下面看一個樣例:
| public static void Main(string[] args) { var host = new WebHostBuilder() .UseServer("Microsoft.AspNetCore.Server.Kestrel") .UseContentRoot(Directory.GetCurrentDirectory()) .UseDefaultConfiguration(args) .UseIISPlatformHandlerUrl() .UseStartup<Startup>() .Build(); host.Run(); } | 
ASP.NET Core host engine 的建立者是 WebHostBuilder.
它實質上是一個了 IWebHostBuilder interface,其中 UseServer() 是用于指定使用什么樣的 server。
其中 UseServer() 有一個擴展方法UseServer(string assemblyName)。這樣的話我們可以直接傳入Kestrel的程序集名稱:”Microsoft.AspNetCore.Server.Kestrel”。當然,這只是其中一種選項。你也可以自己實現一個自己的 server,只要你的 server 實現了 IServerFactory interface 即可。這樣的設計提供了一個很大的彈性空間讓我們自行選擇hosting server(托管服務)。
UseContentRoot()
- 這個擴展方法是讓我們指定應用程序的工作目錄(working directory),如果我們沒有指定的話,則會默認為我們的應用(webapp.exe)所在目錄為工作目錄。 
UseDefaultConfiguration()
- 這個擴展方法使得我們在IWebHostBuilder 建立可以傳入一些參數,比如 application key, environment name, server factory location, content root path 等等.因此,當我們在運行 WebApp.exe 的時候,同時可以帶入我們需要用到的hosting參數(PS:這樣的做法就像運行命令行程序時帶入參數,多好玩)。這些參數也可以寫在appsettings.json里面通過Configuration來讀取。 
 所以,UseDefaultConfiguration() 也不一定非要存在于 Main() 之中。(???個人不是很理解)
PS:原作者原話,”如果我沒記錯的話,在寫這個文章的時候,UseDefaultConfiguration() 已經被改為成了UseDefaultHostingConfiguration().顯然這個名稱更能清楚明白.”(???個人還沒實踐)
UseIISPlatofmrHandleUrl()
- 這個 IWebHostBuilder 的 擴展方法比較特殊。如果你要把 ASP.NET Core 放在 IIS 下,這個擴展方法會讀取 IIS http platform handler 的 server port 和 application path,用于作為 ASP.NET Core 的啟動位置,如http://localhost:5000/start.如果你沒用?IIS,這個擴展方法對你來說基本是用不上的. 
UseStartup<>()
- 這是 WebHostBuilder 里相當重要的一個擴展方法。它的方法簽名如下: 
| 1 2 3 | public static IWebHostBuilder UseStartup<TStartup> (this IWebHostBuilder hostBuilder) where TStartup : class | 
這里你可以很清楚地看到 <> 里面要放的就是一個 class。
在我們這里的范例中,它的名字是 Startup,里面最重要的就是需要定義要使用那些服務(service)以及要使用那些中間件(middleware)。
3、Startup
這是一個非常非常重要的class,在ASP.NET Core范例中一般都把它命名成Startup。其實我們把它命名成其他名字也是可以的,或者設定多個Startup。上面的內容可以看到,UseStartup()指定了誰是starup class。然后在Build()便會實例化starup class,之后便執行里面兩個重要的方法:ConfigureServices() 和 Configure()。
我們先來看 Startup 的構造函數.
| public Startup(IHostingEnvironment env) { // Set up configuration sources. var builder = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build().ReloadOnChanged("appsettings.json"); } | 
Host engine 在被執行 Build()時已經知道 startup type 是 Startup class,所以在 Build() 的時候會先創建期示例。在我們這里的是調用Startup類帶參數的構造函數。
在我們這個例子中選擇的是傳入IHostingEnvironment示例,它為我們帶來了環境變量(EnvironmentName)。
我們這里主要目的是把Configuration實例化。這是一個蠻重要的基礎組件,以后會有文章來說明它。
在這里我們特別說明一下,上面的示例代碼中,我們執行了兩次 AddJsonFile(),而且第二個json file的參數和第一個的還不太一樣。這樣的目的是為了讓開發者可以把開發環境使用的環境參數和其他環境使用的參數有所區別。比如,你使用的開發環境用的是appsettings.json,這個文件只存在于你的電腦中。另一個文檔是appsettings.production.json,這是正式環境使用的參數設定文檔。第二個 AddJsonFile() 第二個參數是 true,也就是可能不存在的意思。所以若遇到重復名稱參數時,appsettings.production.json 會覆蓋 appsettings.json 的內容。這樣使得開發環境和生產環境得以區分。
接下來,在 IWebHostBuilder 的 Build()里面會執行 host engine 初始化的程序,其中就會去找Startup class里面的兩個方法: ConfigureServices() 和 Configure()。
ConfigureSerivces() 是定義了這個 web application 要使用那些服務,然后將這些服務放在 service container (IServiceCollection) 裡面。如下面的樣例:
| public void ConfigureServices(IServiceCollection services) { // add entity framework services.AddEntityFramework() .AddDbContext<BlogsContext>(o => o.UseSqlServer(Configuration["Data1:DefaultConnection:ConnectionString"])) // Add framework services. services.AddMvc(); } | 
它定義了entity framework和mvc兩個服務。這里所謂的服務(services)的意思也就是通過它們帶入更龐大的程序代碼。這聽起來好像有點搞笑,但也真的如此。像Entity framework 里面有這么多的代碼,一定都需要帶入許多定義好的物件或者參數,而不只是一個程序的進去點而已,所以services 的目的就是在這里。
Configure() 主要是定義了中間件(middleware)以及它們的順序.
| public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); } | 
4、Build 和 Run
最后,在IHostWebBuilder里最后的兩個動作便是:Build and Run.
Build()
- 這個方法做的工作便是建立 hosting service,把 Startup 中定義的的 services 和 middleware 接收過來,然后確定content root path 和 application name,接著一句前面這些資料再加上Configuration過來的數據來初始化host engine (WebHost.cs). 
Run()
- 這個是啟動 host engine 的 擴展方法,它在啟動之前加入了一個 CancelKeyPress 的事件.因為在 Run() 方法 中傳入入了 CancellationTokenSource() ,讓我們有一個方法可以隨時中斷host engine的執行。 
目前的做法就用是 CancelKeyPress 事件,所以你可以按下 Ctrl+C 來中止 host engine 的執行.
比較特別的是,這一段中止的文字說明居然是用 hard code,參考如下:
host.Run(cts.Token, “Application started. Press Ctrl+C to shut down.”);
不過這樣的話,這里你也不能寫中文…
本文整理于https://dotblogs.com.tw/aspnetshare/2016/03/28/20160327并已征得作者同意。
感謝Bruce的分享。
原文鏈接:http://codelover.link/2016/07/28/asp.net-core-startup/
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的ASP.NET Core 启动方式(Hosting)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 10月15日 2016中国开源年会期待您
- 下一篇: Lind.DDD.RedisClient
