[ASP.NET Core 3框架揭祕] 跨平臺開發體驗: Windows [中篇]
我們在《上篇》利用dotnet new命令建立了一個簡單的控制檯程式,接下來我們將它改造成一個ASP.NET Core應用。一個ASP.NET Core應用構建在ASP.NET Core框架之上,ASP.NET Core框架利用一個訊息處理管道完成對HTTP請求的監聽、接收、處理和最終的響應。ASP.NET Core管道由一個伺服器(Server)和若干中介軟體(Middleware)構成,當宿主(Host)程式啟動之後,管道被構建出來,作為管道“龍頭”的伺服器開始監聽來自客戶端的HTTP請求。
一、新增引用
接下來我們直接利用Visual Studio 開啟前面這個helloworld.csproj專案檔案。為了能夠使用ASP.NET Core 框架提供的程式集,我們可以通過修改專案檔案(.csproj)新增針對“Microsoft.AspNetCore.App”的框架引用(FrameworkReference)。在Visual Studio中修改專案檔案非常方便,我們只需要右鍵選擇目標專案,並從彈出的選單中選擇“Edit Project File”就可以了。如下所示的是修改後的專案檔案,針對“Microsoft.AspNetCore.App”的框架引用被新增到<ItemGroup/>節點下。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> <ItemGroup> <FrameworkReference Include="Microsoft.AspNetCore.App"/> </ItemGroup> </Project>
二、註冊伺服器與中介軟體
從應用承載或者寄宿(Hosting)方面來看,.NET Core具有一個以IHost/IHostBuilder為核心的服務承載系統,任何需要長時間執行的操作都可以定義成IHostedService服務並通過該系統來承載。IHost物件可以視為所有承載服務的宿主(Host),而IHostBuilder物件則是它的構建者(Builder)。一個ASP.NET Core應用本質上就是一個用來監聽、接收和處理HTTP請求的後臺服務,所以它被定義成一個GenericWebHostService(實現了IHostedService介面),我們將它註冊到承載系統中進而實現了針對ASP.NET Core應用的承載。
一個執行的ASP.NET Core應用本質上體現為由一個伺服器和若干中介軟體構成的訊息處理管道,伺服器解決針對HTTP請求的監聽、接收和最終的響應,具體針對請求的處理則由它遞交給後續的中介軟體來完成。這個管道是由前面提到的GenericWebHostService服務構建出來的。
ASP.NET Core提供了幾種原生的服務型別,比較常用的是KestrelServer和HTTP.sys。KestrelServer是採用libuv建立的跨平臺的Web伺服器,可以在Windows、Mac OS和Linux上使用。它不僅可以作為獨立的Web伺服器直接對外提供服務,也可以結合傳統的Web伺服器(比如IIS、Apache和NGinx)並將它們作為反向代理來使用。HTTP.sys則是一種只能在Windows平臺使用的Web伺服器,由於它本質上是一個在作業系統核心模式執行的驅動,所以能夠提供非常好的效能。
在對專案檔案helloworld.csproj作了上述修改之後,我們對定義在Program.cs中的Main方法做了如下的改造。我們呼叫了靜態型別Host的CreateDefaultBuilder方法建立了一個IHostBuilder物件,並最終呼叫該物件的Build方法構建出作為服務宿主的IHost物件。當我們呼叫IHost物件的Run擴充套件方法的時候,ASP.NET Core應用程式將會被啟動。
在呼叫Build方法構建IHost物件之前,我們呼叫IHostBuilder介面的ConfigureWebHost擴充套件方法,並利用指定的Action<IWebHostBuilder>委託物件構建出ASP.NET Core應用的請求處理管道。具體來說,我們呼叫IWebHostBuilder介面的UseKestrel擴充套件方法將KestrelServer註冊為伺服器,呼叫Configure擴充套件方法註冊了用來處理請求的中介軟體。Configure方法的輸入引數是一個Action<IApplicationBuilder>物件,所需的中介軟體就註冊在IApplicationBuilder物件上。演示程式註冊的唯一中介軟體是通過呼叫IApplicationBuilder介面的Run擴充套件方法註冊的,該中介軟體利用指定的Func<HttpContext,Task>物件將響應的主體內容設定為“Hello World”。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; namespace helloworld { class Program { static void Main() { Host.CreateDefaultBuilder() .ConfigureWebHost(webHostBuilder => webHostBuilder .UseKestrel() .Configure(app => app.Run( context => context.Response.WriteAsync("Hello World.")))) .Build() .Run(); } } }
我們可以按照上面演示的那樣通過執行dotnet命令列來啟動該程式,也可以直接在Visual Studio中按F5或者Ctrl+F5啟動該程式。下圖所示的是執行dotnet run命令後控制檯的輸出結果,這些輸出其實是通過日誌的形式輸出的。我們從這些輸出可以看出ASP.NET Core應用採用的預設監聽地址(http//localhost:5000和https//localhost:5001)和承載環境(Production)。如果需要關閉應用程式,只需要按Ctrl+C組合鍵就可以了。
註冊的KestrelServer伺服器會繫結到http//localhost:5000和https//localhost:5001這兩個地址監聽請求,如果我們利用瀏覽器分別對這兩個地址發起請求會得到怎樣的響應呢?如下圖所示,兩個請求都會得到主體內容為“Hello World.”的響應(由於證書的問題,Chrome瀏覽器為HTTPS的請求會顯示“Not secure”的警告),毫無疑問該內容就是我們註冊的中介軟體寫入的。
三、修改SDK
每個.NET Core應用都針對一種具體的SDK型別。我們在前面展示了專案檔案helloworld.csproj的完整定義,這是一個XML檔案,根節點的<Project>上通過SDK屬性設定了當前專案採用的SDK型別。對於前面這個通過dotnet new命令工具創建出來的控制檯應用,它預設採用的SDK型別為“Microsoft.NET.Sdk”。對於一個ASP.NET Core應用,我們一般會採用另一種名為“Microsoft.NET.Sdk.Web”的SDK型別。
如果將SDK設定為“Microsoft.NET.Sdk.Web”,我們甚至可以將針對“Microsoft.AspNetCore.App”的框架引用從專案檔案中刪除。由於我們並不需要利用生成的.exe檔案來啟動ASP.NET Core應用,所以我們也應該將XML元素<OutputType>Exe</OutputType>從<PropertyGroup>節點中刪除,所以最終的專案檔案只需要保留如下的內容就可以了。
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> </Project>
四、launchSettings.json
當我們通過修改專案檔案helloworld.csproj將SDK改為“Microsoft.NET.Sdk.Web”之後,如果我們使用Visual Studio開啟這個檔案,一個名為“launchSettings.json”的配置檔案將自動生成並被儲存在“\Properties”目錄下。顧名思義,launchSettings.json是一個在應用啟動的時候自動載入的配置檔案,該配置檔案使我們可以在不同的設定下執行我們的應用程式。如下所示的就是Visual Studio自動建立的launchSettings.json檔案的內容。我們可以看出該配置檔案預設添加了兩個節點,其中“iisSettings”用於設定IIS相關的選項,而“profiles”節點定義了一系列用於表示應用啟動場景的Profile。
{ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:51127/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "helloworld": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:51128/" } } }
初始的launchSettings.json檔案會預設建立兩個Profile,一個被命名為“IIS Express”,另一個則使用當前專案名稱來命名(“helloworld”)。每一個Profile相當於定義了應用的啟動場景,相關的設定包括應用啟動的方式、環境變數和URL等,具體的設定包括:
- commandName:啟動當前應用程式的命令型別,有效的選項包括IIS、IISExpress、Executable和Project,前三個選項分別表示採用IIS、IISExpress和指定的可執行檔案(.exe)來啟動應用程式。如果我們使用dotnet run命令來啟動程式,對應Profile的啟動命名名稱應該設定為Project。
- executablePath:如果commandName屬性被設定為Executable,我們需要利用該屬性來設定啟動可執行檔案的路徑(絕對路徑或者相對路徑)。
- environmentVariables:該屬性用來設定環境變數。由於launchSettings.json僅僅使用在開發環境,所以預設會新增一個名為“ASPNETCORE_ENVIRONMENT”的環境變數,並將它的值設定為“Development”,ASP.NET Core應用中正是利用這樣一個環境變數來表示當前的部署環境。
- commandLineArgs:命令列引數,即傳入Main方法的引數列表。
- workingDirectory:啟動當前應用執行的工作目錄。
- applicationUrl:應用程式採用的URL列表,多個URL之間採用分號(“;”)進行分隔。
- launchBrowser:一個布林型別的開關,表示應用程式的時候是否自動啟動瀏覽器。
- launchUrl:如果launchBrowser被設定為true,瀏覽器採用的初始化路徑通過該屬性進行設定。
- nativeDebugging:是否啟動原生代碼除錯(Native Code Debugging),預設值為false。
- externalUrlConfiguration:如果該屬性被設定為true,意味著禁用本地的配置,預設值為false。
- use64Bit:如果commandName屬性設定為IIS Express,該屬性決定是採用X64版本還是X86版本,預設值為false,意味著ASP.NET Core應用預設會採用X86版本的IIS Express。
launchSettings.json檔案中的所有設定僅僅針對開發環境,產品環境下是不需要這個檔案的,應用釋出後生成的檔案列表中也不包含該檔案。該檔案不需要手工進行編輯,當前專案屬性對話方塊(通過在解決方案對話方塊中右擊選擇“屬性(Properties)”選項)中“除錯(Debug)”選項卡下的所有設定最終都會體現在該檔案上。
如果在launchSettings.json檔案設定了多個Profile,它們會以如下圖所示的形式出現在Visual Studio的工具欄中,我們可以選擇任意一個Profile中定義的配置選項來啟動當前應用程式。如果Profile中通過設定launchBrowser屬性選擇啟動瀏覽器,我們還可以選擇瀏覽器的型別。
如果我們在當前專案所在目錄下通過執行dotnet run命令來啟動應用程式,launchSettings.json檔案會預設被載入。我們可以通過命令列引數--launch-profile指定採用的Profile。如果沒有對Profile作顯式指定,定義在該配置檔案中第一個commandName為“Project”的Profile會預設被使用。如下圖所示,我們在建立的應用根目錄下通過執行dotnet run命令啟動我們的應用程式,其中第一次執行dotnet run命令的時候顯式設定了Profile名稱(--launch-profile helloworld)。從輸出的結果可以看出,兩次採用的是同一個Profile。
如果在執行dotnet run命令的時候不希望載入launchSettings.json檔案,我們可以通過顯式指定命令列引數--no-launch-profile來實現。如下圖所示,我們在執行dotnet run命令時指定了--no-launch-profile引數,所以應用會採用KestrelServer預設的監聽地址(http://localhost:5000和https://localhost:5001)。由於launchSettings.json根本就沒有被載入,所以當前執行環境從Development變成了預設的Production。
五、顯式指定URL
如果既不想使用launchSettings.json檔案中定義的URL,也不想使用KestrelServer預設採用的監聽地址,我們可以在應用程式中顯式指定應用的URL。如下面的程式碼片段所示,我們只需要呼叫IWebHostBuilder的擴充套件方法UseUrls指定一組以分號分隔的URL即可。
class Program { static void Main() { Host.CreateDefaultBuilder() .ConfigureWebHost(webHostBuilder => webHostBuilder .UseKestrel() .UseUrls("http://0.0.0.0:3721;https://0.0.0.0:9527") .Configure(app => app.Run(context => context.Response.WriteAsync("Hello World.")))) .Build() .Run(); } }
六、ConfigureWebHostDefaults
在上面演示的例項中,我們都是呼叫IHostBuilder介面的ConfigureWebHost擴充套件方法藉助指定的Action<IWebHostBuilder>委託物件來構建處理請求處理管道,該介面還有另一個ConfigureWebHostDefaults的擴充套件方法,它會為我們作一些預設設定。如下面的程式碼片段所示,如果呼叫這個方法,KestrelServer伺服器都不需要進行顯式註冊。
class Program { static void Main() { Host.CreateDefaultBuilder() .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder.Configure(app => app.Run(context => context.Response.WriteAsync("Hello World.")))) .Build() .Run(); } }
[ASP.NET Core 3框架揭祕] 跨平臺開發體驗: Windows [上篇]
[ASP.NET Core 3框架揭祕] 跨平臺開發體驗: Windows [中篇]
[ASP.NET Core 3框架揭祕] 跨平臺開發體驗: Windows [下篇]
[ASP.NET Core 3框架揭祕] 跨平臺開發體驗: Mac OS
[ASP.NET Core 3框架揭祕] 跨平臺開發體驗: Linux
[ASP.NET Core 3框架揭祕] 跨平臺開發體驗: Docker