微軟在千禧年推出 .NET戰略,併在兩年後推出第一個版本的.NET Framework和IDE(Visual Studio.NET 2002,後來改名為Visual Studio),如果你是一個資深的.NET程式員,相信傳統的.NET應用的開發方式已經深深地烙印在你的腦子裡面。.NET Core打來... ...
微軟在千禧年推出 .NET戰略,併在兩年後推出第一個版本的.NET Framework和IDE(Visual Studio.NET 2002,後來改名為Visual Studio),如果你是一個資深的.NET程式員,相信傳統的.NET應用的開發方式已經深深地烙印在你的腦子裡面。.NET Core打來了全新的開發體驗,但是開發方式的差異根本不足以成為你快速跨入.NET Core 世界的門檻,因為在.NET Core在很多方面比傳統的.NET Framework應用開發要簡單。為了消除很多尚未接觸過.NET Core的讀者對未知世界的恐懼,我們先通過幾個簡單的Hello World應用讓大家感受一下.NET Core全新的開發體驗。
目錄
一、安裝開發環境
二、利用命令行創建.NET Core程式
三、改造成一個ASP.NET Core應用
四、進一步改造成ASP.NET Core MVC應用
不管你是否已經接觸過.NET Core,我們相信你一定知道.NET Core是“跨平臺”的。較之傳統的.NET Framework應用只能運行在微軟自家的Windows平臺上,經過全新設計的.NET Core在誕生的時候就被註入了跨平臺的基因,通過.NET Core應用在無需經過任何更改的情況下就可以直接運行在Windows、Mac OS以及各種Linux Distribution(RHEL、Ubuntu、Debian、Fedora、CentOS和SUSE等)平臺上。除此之外,.NET Core針對Docker也提供了原生的支持,一個.NET Core應用可以同時運行在Windows Container和Linux Container上。我們接下里現在Windows平臺下感受一下.NET Core的開發體驗,不過在這之前先得構建一下開發環境。
一、安裝開發環境
.NET Core的官方站點(https://www.microsoft.com/net/core)提供了在各種平臺下安裝開發環境的介紹。總的來時,我們在不同的平臺下開發.NET Core應用都需要按照相應的SDK和IDE。針對Windows開發平臺來說,.NET Core 2.0.0 SDK可以通過上述這個站點直接下載。成功安裝SDK之後,我們在本地將自動擁有了.NET Core的運行時(CoreCLR)、基礎類庫以及相應的開發工具。
dotnet.exe是.NET Core SDK為我們提供的一個重要的命令行工具,我們在進行.NET Core應用的開發部署的時候將會頻繁地使用它。dotnet.exe提供了很多有用的命令,為了不“節外伸枝”,我們就不對它們作系統介紹了,如果後續章節涉及到相關命令的時候,我們再對它們作針對性的介紹。當.NET Core SDK安裝結束之後,我們可以運行dotnet命令來確認SDK是否安裝成功。如下圖1所示,我們執行dotnet --info命令查看當前安裝的.NET Core SDK的基本信息,其中包含SDK的版本、運行時環境和共用框架宿主的版本信息。
高效的開發自然離不開一個優秀的IDE,在這方面作為一個.NET程式員是幸福的,因為我們擁有宇宙第一個開發神器Visual Studio。雖然Visual Studio Code也不失為一個優秀的IDE,如果在絕大部分情況下Windows還是主要的開發環境,我個人還是推薦使用Visual Studio。當我們在敲這行文字的時候,Visual Studio的最新版本為2017(15.3)。順便說一下,Visual Studio已經提供了Mac版本。
Visual Studio Code是一個完全免費並且提供全平臺支持(Windows、Mac和Linux)的IDE,我們可以直接在其官網(https://code.visualstudio.com/)上下載。Visual Studio 2017提供了社區版(Community)、專業版(Professional)和企業版(Enterprise),其中社區版是免費的,專業版和企業版需要付費購買。Visual Studio的官網地址為https://www.visualstudio.com/。
除了Visual Studio和Visual Studio Code,我們還可以一款叫做Rider的IDE來開發.NET Core應用。Rider是著名的JetBrains公司開發的一款專門針對.NET的IDE,我們可以利用它來開發ASP.NET、.NET Core、Xmarin以及Unity應用。和Visual Studio Code一樣,Rider同樣也是個跨平臺的IDE,我們可以同時在Windows、Max OS X以及各種桌面版本的Linux Distribution上使用它。不過這不是一款免費的IDE,對它感興趣的朋友可以在官方站點(https://www.jetbrains.com/rider/)下載30天試用版。
二、利用命令行創建.NET Core程式
通過.NET Core SDK在本地安裝的dotnet 工具提供了基於預定義“腳手架(Scaffolding)”創建初始應用的命令(new)。如果需要開發某種類型的.NET Core應用,我們一般不會從第一行代碼寫起,而是利用這個命令幫助我們創建一個具有初始結構的應用。除此之外,在開發過程中如果需要添加某種類型的文件(比如各種類型的配置文件、MVC的視圖文件等),我們也可以利用該命令來完成,通過這種方式添加的文件具有預定義的初始內容。.NET Core SDK在安裝的時候為我們提供了一系列預定義的腳手架模板,我們可以按照如下圖所示的方式執行命令行“dotnet new list”列出當前安裝的腳手架模板。
上圖列出就是NET Core 2.0.0 SDK安裝後提供的16個預定義的腳手架模板,這些模板大致分為Project Template和Item Template兩種類,前者為我們創建一個初始項目,後者則在一個現有項目中針對某種項目元素添加一個或者多個對應的文件。對於細心的讀者,可以從上圖中看到dotnet new命令具有一個--type參數,該參數具有三個預定義的選項(project、item和other),其中前兩個分別對應著Project和Item這兩種模板類型。
如果這些預定義的腳手架模板不能滿足我們的需求,我們還可以根據自身的需要創建自定義的Project或者Item模板,至於自定義模板的該如何定義,我們就不在這裡贅言介紹了,有興趣的讀者朋友可以參考.NET Core官方文檔(https://docs.microsoft.com/en-us/dotnet/core/tools/custom-templates)。我們創建的自定義模板最終體現為一個NuGet包,我們可以通過執行dotnet new -i或者dotnet new --install命令對其進行安裝。除此之外,對應已經安裝的模板,我們可以通過執行dotnet new -u或者dotnet new --uninstall命令卸載。
接下來我們利用dotnet new命令(dotnet new console -n helloworld)按照如下圖所示的方式創建一個名為“helloworld”的控制台程式。和傳統的.NET Framework應用,一個針對C#的.NET Core項目依然由一個對應的.csproj文件來定義,下圖所示的helloworld.csproj就是這麼一個文件。
對於傳統的.NET Framework應用來說,即使是一個空的C#項目,定義該項目的.csproj文件在內容和結構上顯得比較複雜。這個.csproj文件的結構並不是為一般的開發者設計的,我們也不會直接編輯這個文件,而是利用Visual Studio間接地修改它。但是對於一個.NET Core應用來說,這個.csproj文件的結構變得相對簡單並清晰了一些,以至於一般的開發人員可以直接編輯它。對於上面我們執行腳手架命令創建的控制台程式,定義項目的這個helloworld.csproj文件的完整內容如下。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup> </Project>
如上面的代碼片段所示,這個helloworld.csproj是一個根節點為<Project>的XML文件,與項目相關的屬性可以根據分組定義在相應的<PropertyGroup>節點下。對於這個helloworld.csproj文件來說,它實際上只定義了兩個屬性,分別是通過<OutputType>和<TargetFramework>節點表示的編譯輸出類型和目標框架類型。由於我們創建的是一個針對.NET Core 2.0的可執行控制台應用,所以目標框架為“netcoreapp2.0”,編譯輸出為Exe(對於Self Contained發佈模式)。
我們執行的dotnet new命令行除了幫助我們創建一個空的控制台程式之外,還會幫助我們生成一些初始化代碼,這就是項目目錄下的這個Program.cs文件的內容。如下所示的代碼片段給出了定義在這個文件的整個C#代碼的定義,我們可以看到它定義了代表程式入口點的Main方法,併在這個方法中將字元串“Hello World”列印在控制臺上。
using System; namespace helloworld { class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } }
雖然很簡單,但是我們通過執行腳手架命令行創建出來的是一個完成的.NET Core控制台應用,所以我們可以在無需對其作任何修改的情況下直接對它進行編譯和運行,針對.NET Core應用的編譯和運行同樣是利用這個dotnet.exe命令行來完成的。如下圖所示,在進入當前項目所在目錄之後,我們執行dotnet build命令對這個控制台應用實施編譯,由於預設採用Debug編譯模式,所以編譯生成的程式集(helloworld.dll)會保存在\bin\debug\目錄下。除此之外,針對不同目標框架編譯生成的程式集是不同的,所以最終生成的程式集會採用基於目標框架的目錄結構進行組織,所以最終生成的這個程式集被保存在“\bin\Debug\netcoreapp2.0\”目錄下。
編譯後的控制台程式可以直接通過執行dotnet run命令運行。如下圖所示,當我們在項目目錄下執行dotnet run命令後,編譯後的程式隨被執行,程式入口Main方法中指定的“Hello World”字元串被直接列印在控制臺上。其實當我們執行dotnet run命令啟動程式之前無需顯示執行dotnet build對源代碼實施便宜,因為該命令會自動觸發編譯操作。
三、改造成一個ASP.NET Core應用
我們在上面利用dotnet new命令創建了一個簡單的控制台程式,接下來我們將它改造成一個ASP.NET Core應用。一個ASP.NET Core應用構建在ASP.NET Core框架之上,後者利用一個管道式的構建完成了對HTTP請求的監聽、接收、處理和最終的響應,實際上本書的所有內容就是圍繞著管道展開的。ASP.NET Core管道由一個伺服器和若幹中間件構成,當宿主(Host)程式啟動之後,該管道被成功構建出來並利用伺服器開始HTTP請求的監聽。
從編程層面來看,ASP.NET Core管道的構建主要涉及WebHost和WebHostBuilder這兩個對象。我們可以根據命名將WebHost理解為Web應用的宿主,WebHost是由WebHostBuilder構建出來的。一般來說,我們會先創建一個WebHostBuilder對象,並將最終管道構建所需的各項設置通過相應的方法(絕大部分是擴展方法)註冊到它上面。註冊完成之後,我們直接利用這個WebHostBuilder創建出對應的WebHost,當後者被啟動的時候,整個管道會根據我們預定義的設置被構建出來。
接下來我們直接利用Visual Studio 2017打開上面這個helloworld.csproj項目文件。為了在程式性中使用到上述這兩個對象,我們自然先得具有對應程式集的引用。對於.NET Core應用來說,所有的程式集都會封裝到相應的NuGet包中進行分發,如果需要消費某個框架或者類庫,我們都需要預先安裝相應的NuGet包。至於NuGet包的安裝,我們有很多的方式可以選擇。
安裝NuGet包
WebHostBuilder所在的程式集包含在Microsoft.AspNetCore.Hosting這個NuGet包中,接下來我們就以它為例介紹若幹中不同的NuGet包的安裝方式。如果使用Visual Studio 2017來開發.NET Core應用,我們最常使用的是由IDE提供的可視化NuGet安裝方式。如果按照這種安裝方式,我們只需要在“解決方案管理器(Solution Explorer)”視窗中右鍵選擇對應的項目或者項目下的“依賴(Dependencies)”節點,併在彈出的上下文菜單中選擇“管理NuGet包(Manage NuGet Packages)”選項就可以打開如下圖所示的“NuGet包管理視窗”。
如上圖所示,“NuGet包管理視窗”具有三個標簽頁,其中“安裝(Installed)”和“更新(Updates)”會列出當前項目已經安裝和可以升級的NuGet包。如果我們需要按照一個新的NuGet包,我們需要選擇第一個標簽頁,併在左上角的文本框中輸入需要安裝的NuGet包的全名或者全名的部分文字,與之相關的NuGet包將會篩選出來,如果目標NuGet包正好在該列表中,我們只需要選擇它並點擊右側的“安裝(Install)”按鈕即可。
除了採用上述這種完全可視化的方式來安裝NuGet包,Visual Studio還提供了一種命令行的安裝方式。包括安裝在內的NuGet包管理命令是在Visual Studio的“包管理器控制台(Package Manager Console)”視窗中輸入並執行的,我們可以通過菜單“工具(Tools)>選項(Options)>NuGet包管理器(NuGet Package Manager)>包管理器控制台(Package Manager Console)”開啟如下圖所示的這個視窗。如下圖所示,我們通過輸入並執行NuGet包安裝命令“Install-Package Microsoft.AspNetCore.Hosting -Version 2.0.0”安裝版本為2.0.0的NuGet包“Microsoft.AspNetCore.Hosting”。
除了上面介紹的這兩種在Visual Studio開發環境中提供的NuGet包的安裝放之外,我們已經很熟悉的這個dotnet命令行工具同樣提供了安裝NuGet包的支持。具體來說,我們可以執行dotnet add package命令安裝指定的NuGet包。如下圖所示,在確保當前為目標項目所在目錄的前提下,我們所需的NuGet包同樣可以通過執行“dotnet add package Microsoft.AspNetCore.Hosting --version 2.0.0”命令來完成。
有的情況下我們往往只知道某個需要使用的類型名稱而忘記了所在NuGet包的名字,如果你使用了Visual Studio 2017,可以藉助它提供的智能提示功能來安裝對應的NuGet包。很多人都體現過Visual Studio針對命名空間的自動補齊特性,當我們在C#編輯視窗直接輸入一個尚未導入命名空間的類型名稱的時候,一旦我們將滑鼠落在該類型上面的時候,Visual Studio會自動出現如下圖所示的“燈泡”圖標,點擊該圖標之後會出現一組候選的命名空間(這組候選命名空間菜單也可以通過快捷鍵Ctrl+Alt+F10開啟)。
這個特性在Visual Studio 2017上做了進一步改進。如果我們輸入了某個類型的名稱,但是所在的NuGet包尚未被安裝,Visual Studio可以利用這個特性智能地提示這個缺失的NuGet包的名稱。如下圖所示,在尚未安裝“Microsoft.AspNetCore.Hosting”這個NuGet包的情況下,我們在C#編輯視窗中輸入WebHostBuilder這個類型,Visual Studio會利用類似的特性提示我們安裝缺失的NuGet包。
對於對上述的眾多NuGet包的安裝方式,它們最終的目的實際上就是在描述當前項目的.csproj問文件中添加一個針對指定NuGet包的引用而已。如下所示的代碼片段代表“Microsoft.AspNetCore.Hosting”這個NuGet包被成功安裝後的內容,可以看出針對某個NuGet包的引用總是對應著.csproj文件中的某個< PackageReference>節點。
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.0.0" /> </ItemGroup> </Project>
既然安裝NuGet包的最終目的就是在所在項目的.csproj文件中添加一條對應的< PackageReference>節點,那麼我們完全可以直接通過修改此文件的方式來完整針對NuGet包的安裝。在過去,如果我們想直接利用Visual Studio編輯某個項目對應的.cspro文件,我們必須先採用如下圖(左圖和中圖)的方式將對應的項目卸載。對於.NET Core應用開發來說,直接編輯項目文件已經變成了意見司空見慣的事情,所以Visual Studio 2017允許我們按照下圖(右圖)所示的方式直接對項目文件進行編輯。
對於.NET Core來說,提供API的程式集總是通過相應的NuGet包來提供,所以NuGet包的安裝成為了我們最為頻繁的操作之一。除此之外,我們在IDE上也有了更多的選擇,所以微軟提供了眾多NuGet包的管理方式供我們在不同的開發環境中選擇。綜上所述,我們可以通過如下的方式進行NuGet包的安裝:
- 利用Visual Studio的NuGet包管理器(NuGet Package Manager)進行可視化安裝。
- 在Visual Studio提供的包管理器控制台(Package Manager Console)以命令行的方式安裝NuGet包。
- 通過執行dotnet add package以命令行的形式安裝NuGet包。
- 通過修改定義項目的.csproj文件安裝的方式安裝NuGet包。
在介紹瞭如何安裝NuGet包之後,我們回到本行最初的話題:如何將通過腳手架命令創建的控制台應用轉化成一個ASP.NET Core應用。我們在前面已經提到過,ASP.NET Core應用建立在由一個伺服器和若幹中間件組成的管道上,最初對HTTP請求的監聽以及最終對該請求的響應都由伺服器完成。微軟為我們提供了若幹原生的伺服器供我們選擇,如果對應用具有跨平臺的要求,我們可以選擇一個名為KestrelHttpServer的伺服器。該伺服器類型定義在NuGet包“Microsoft.AspNetCore.Server.Kestrel”中,所以我們還需要安裝這個NuGet包。
註冊伺服器與中間件
在安裝了所需的NuGet包(“Microsoft.AspNetCore.Hosting”和“Microsoft.AspNetCore.Server.Kestrel”),我們對定義在Program.cs中的Main方法做瞭如下的改造,這應該算是一個最為簡單的ASP.NENT Core應用了。我們首先創建了一個WebHostBuilder對象並調用其擴展方法UseKestrel註冊了上述的這個類型為KestrelHttpServer的伺服器。接下來我們調用了WebHostBuilder的Configure方法,該方法接受一個Action<IApplicationBuilder>類型的委托對象作為參數,我們利用這個委托對象調用IApplicationBuilder的Run方法註冊了一個中間件,後者從事的唯一操作就是在響應中寫入了一個內容為“Hello World”的字元串。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; namespace helloworld { class Program { static void Main() { new WebHostBuilder() .UseKestrel() .Configure(app => app.Run( async context => await context.Response.WriteAsync("Hello World"))) .Build() .Run(); } } }
在將所需的伺服器和中間件註冊到創建的WebHostBuilder對象之後,我們調用其Build方法創建了對應的WebHost對象, 當我們調用後者的Run方法之後,真正的ASP.NET Core管道被構建出來。由於註冊的KestrelHttpServer伺服器預設採用5000作為監聽埠,所以程式在啟動之後將綁定在這個埠監聽抵達的HTTP請求。如果利用瀏覽器訪問http://localhost:5000,我們會得到由註冊中間件寫入的“Hello World字元串”。
如果預設使用的5000埠不可用,或者不希望使用這個預設的埠,我們還可以調用WebHostBuilder的擴展方法UseUrls註冊新的監聽地址。值得一提的是,我們可以同時指定多個監聽地址,下麵的代碼片段就為KestrelHttpServer分別註冊了兩個埠號分別為6666和8888的監聽地址。
new WebHostBuilder() .UseUrls("http://localhost:6666", "http://localhost:8888") .UseKestrel() .Configure(app => app.Run(async context => await context.Response.WriteAsync("Hello World"))) .Build() .Run();
四、進一步改造成ASP.NET Core MVC應用
對於我們在上面一節創建的這個極簡ASP.NET Core應用來說,它對應的管道由一個伺服器和一個中間件組成,前者的類型為KestrelHttpServer,後者則將每個請求的響應內容統一設置為“Hello World”字元串。接下來我們對該應用做進一步地改造,將它轉變成一個ASP.NET Core MVC應用。整個ASP.NET Core MVC框架建立在一個名為RouterMiddleware的中間件上,它利用該中間件提供的路由功能實現了請求URL與目標Controller類型以及Action方法之間的映射,並再此基礎上實現了諸如Controller的激活、Action方法的執行以及View的呈現等操作。
整個ASP.NET Core MVC框架實現在“Microsoft.AspNetCore.Mvc”這個NuGet包中,所以我們先得將它安裝到我們的控制台項目上,然後才能進行針對ASP.NET Core MVC的編程。不過在這之前我們需要瞭解一個關於“依賴註入(Dependency Injection)”。ASP.NET Core框架內置了一個原生的依賴註入框架,後者利用一個DI容器提供管道在構建以及進行HTTP請求處理過程中所需的所有服務,而這些服務需要在應用啟動的時候被預先註冊。對於ASP.NET Core MVC框架來說,它在處理HTTP請求的過程中所需的一系列服務同樣需要預先註冊。對這個概念有了基本的瞭解之後,相信讀者朋友們對如下所示的代碼就容易理解了。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; namespace helloworld { class Program { static void Main(string[] args) { new WebHostBuilder() .UseKestrel() .ConfigureServices(svcs => svcs.AddMvc()) .Configure(app => app.UseMvc()) .Build() .Run(); } } public class HelloController { [HttpGet("/hello")] public string SayHello() { return "Hello World"; } } }
如果說上面一節創建的屬於最簡單的ASP.NET Core應用的話,那麼通過上面這段代碼創建的恐怕就是一個簡單的ASP.NET Core MVC應用了。我們在這個程式中調用了WebHostBuilder的方法ConfigureServices(這是一個擴展方法,其參數類型為Action<IServiceCollection>)註冊ASP.NET Core MVC框架所需的服務,具體來說這些服務是通過調用IServiceCollection介面的擴展方法AddMvc來完成的。在針對Configure方法的調用中,我們調用IApplicationBuilder的擴展方法UseMvc註冊了RouterMiddleware中間件以及針對ASP.NET Core MVC的路由處理器,後者接收路由分發的HTTP請求和路由參數,併在此基礎上實現對Controller的激活、Action方法的執行以及View的呈現等操作。
HelloController是我們定義的Controller類型。按照約定,所有的Controller類型名稱都應該以“Controller”字元作為尾碼。與之前版本的ASP.NET MVC不同的是,ASP.NET Core MVC下的Controller類型並不要求強制繼承某個基類。在HelloController中,我們定義了一個唯一無參Action方法SayHello,該方法直接返回一個內容為“Hello World”的字元串。我們在這個方法上利用HttpGetAttribute註冊了一個模板為“/hello”的路由,意味著請求地址為“/hello”的GET請求最終會被路由到這個Action方法上,後者執行的結果將作為請求的響應內容。所以啟動該程式後使用瀏覽器訪問地址“http://localhost:5000/hello”,我們依然會得到如圖1-13所示的輸出結構。
引入View
上面這個程式並沒有涉及View,所以算不上一個典型的ASP .NET Core MVC,接下來我們對它做進一步改造。在前面介紹如何安裝NuGet包的時候,我們曾經查看過定義項目的 .csproj文件的內容,實際上這是一個以<Project>作為根節點的XML文件。作為根節點的<Project>元素具有一個Sdk的屬性表示當前項目針對的SDK,不同的SDK在編譯發佈等方面對項目提供了不同的支持。當我們安裝了.NET Core SDK的時候,這些針對項目的SDK將被安裝在“%programfiles% \dotnet\sdk\{version}\Sdks”目錄下(如下圖所示)。
對於上面這個通過腳手架命令行創建的控制台應用來說,它預設採用的SDK為“Microsoft.NET.Sdk”。如果我們需要為這個應用添加View,這個SDK並不能提供針對View的動態編譯功能(在運行時動態編譯使用到的View,而不是在部署的時候將所有View進行預編譯),所以我們按照如下的方式編輯.csproj文件將SDK切換為“Microsoft.NET.Sdk.Web”。實際上不論是我們利用Visual Studio還是命令行創建的ASP.NET Core應用,項目都會採用這個SDK。
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.0" /> </ItemGroup> </Project>
在對SDK作了相應修改之後,我們對現有的程式作瞭如下的改造。當編譯器在對View進行動態編譯的時候,需要按照預定義的路徑去定位定義View的.cshtml文件,這些預定義的候選路徑都是相對路徑,所以我們需要預先指定一個基礎路徑,該路徑可以通過調用WebHostBuilder的擴展方法UseContentRoot來指定。如下麵的代碼片段所示,我們將當前路徑(當前項目的根目錄)作為這個基礎路徑。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using System.IO; namespace helloworld { class Program { static void Main() { new WebHostBuilder() .UseContentRoot(Directory.GetCurrentDirectory()) .UseKestrel() .ConfigureServices(svcs => svcs.AddMvc()) .Configure(app => app.UseMvc()) .Build() .Run(); } } public class HelloController: Controller { [HttpGet("/hello/{name}")] public IActionResult SayHello(string name) { ViewBag.Name = name; return View(); } } }
為了讓HelloController具有View呈現的能力,我們讓它派生於基類Controller。Action方法SayHello的返回類型被修改為IActionResult介面,後者表示Action方法執行的結果。除此之外,我們為該方法定義了一個表示姓名的參數name,通過HttpGetAttribute特性註冊的路由模板(“/hello/{name}”)中具有與之對應的路由參數。換句話說,滿足該路由規則的請求URL攜帶的命名將自動綁定為該Action方法的name參數。具體在SayHello方法中,我們利用ViewBag將代碼姓名的name參數的值傳遞給將要呈現出來的View,該View正式執行View方法返回的結果。
由於我們調用View方法時並沒有顯式指定名稱,ASP.NET Core MVC的View引擎會自動將當前Action的名稱(“SayHello”)作為View的名稱。如果該View還沒有經過編譯(部署時針對View的預編譯,或者在這之前針對該View的動態編譯),View引擎將從若幹候選的路徑中讀取對應的.cshtml 文件進行編譯,其中首選的路徑為“{ContentRoot}\Views\{ControllerName}\{ViewName}.cshtml”。為了迎合View引擎定位View文件的規則,我們需要將SayHello對應的View文件(SayHello.cshtml)定義在目錄“\Views\Hello\”下(如下圖所示)。
如下所示的就是SayHello.cshtml這個文件的內容,這是一個針對Razor引擎的View文件。從文件的擴展名(.cshtml)我們可以這樣的文件可以同時包含HTML標簽和C#代碼。總的來說,View文件最終是為了在服務端渲染出最終在瀏覽器呈現出來的HTML,我們可以在這個文件中直接編寫原樣輸出的HTML標簽,也可以內嵌一段動態執行的C#代碼。雖然Razor引擎對View文件的編寫制定了嚴格的語法,但是我個人覺得沒有必要在Razor語法上花太多的精力,因為Razor語法的目的就是讓我們很“自然”地融合動態C#代碼和靜態HTML標簽來定義最終在客戶端渲染的HTML文檔,因此它的語法和普通的思維基本上是一致。比如下麵這個View最終會生成一個完整的HTML文檔,其主體部分只有一個<h3>標簽。該標簽的內容是動態的,因為包含從Controller利用ViewBag傳進來的姓名。
<html> <head> <title>Hello World</title> </head> <body> <h3>Hello, @ViewBag.Name</h3> </body> </html>
再次運行該程式後,我們利用瀏覽器訪問地址“http://localhost:5000/hello/foobar”。由於請求地址與Action方法SayHello上的路由規則相匹配,所以URL上的foobar會被解析為姓名綁定到該方法的name參數上,所以我們最終將在瀏覽器上得到如下圖所示的輸出結果。
利用Startup註冊服務和中間件
對於幾乎所有的ASP.NET Core應用來說,應用啟動過程中必須完成的初始化操作都包括服務與中間件的註冊。在上面演示的實例中,我們都是直接調用WebHostBuilder的ConfigureServices和Configure方法來完成針對服務和中間件的註冊,但是在大部分真實的開發場景中,我們一般會將這兩種類型的註冊定義在一個單獨的類型中。按照約定,我們通常會將這個類型命名為Startup,比如我們演示實例中針對ASP.NET Core MVC的服務註冊和中間件註冊就可以放在如下定義的這個Startup類中。
public class Startup { public void ConfigureServices(IServiceCollection svcs) { svcs.AddMvc(); } public void Configure(IApplicationBuilder app) { app.UseMvc(); } }
如上面的代碼片段所示,我們不需要讓Startup類實現某個預定義的介面或者繼承某個預定義基類,所採用的完全是一種“約定”,隨著對ASP.NET Core框架認識的加深,我們會發現這種“約定優於配置”的設計廣泛地應用在整個框架之中。按照約定,服務註冊和中間件註冊分別實現在ConfigureServices和Configure方法中,它們第一個參數類型分別為IServiceCollection和IApplicationBuilder介面。現在已經將兩種核心的註冊操作轉移到了上面這個Startup類中,那麼我們需要將該類型按照如下的方式調用UseStartup<T>方法註冊到WebHostBuilder上即可。
new WebHostBuilder() .UseContentRoot(Directory.GetCurrentDirectory()) .UseKestrel() .UseStartup<Startup>() .Build() .Run();
.NET Core多平臺開發體驗
.NET Core多平臺開發體驗[1]: Windows
.NET Core多平臺開發體驗[2]: Mac OS X
.NET Core多平臺開發體驗[3]: Linux
.NET Core多平臺開發體驗[4]: Docker