本文將對微軟下一代ASP.NET框架做個概括性介紹,方便大家進一步熟悉該框架。
在介紹ASP.NET Core 1.0之前有必要澄清一些產品名稱及版本號。ASP.NET Core1.0是微軟下一代ASP.NET 框架,在這之前ASP.NET版本穩定在ASP.NET 4.6,對應的.NET Framework版本為.net 4.6.1。
曾經一段時間微軟將下一代ASP.NET 命名為ASP.NET 5和MVC 6,在ASP.NET 5 is dead – Introducing ASP.NET Core 1.0 and .NET Core 1.0一文中,微軟第一次提到ASP.NET 5將會改為ASP.NET Core1.0。
該文還指出了.NET其他產品命名變化
- ASP.NET 5 現在叫做 ASP.NET Core 1.0
- .NET Core 現在叫做 .NET Core 1.0
- Entity Framework 7 現在叫做 Entity Framework Core 1.0 或者簡稱 EF Core 1.0
之所以有這樣的改變,微軟解釋為:下一代的ASP.NET並不是ASP.NET 4.6的簡單升級,如果命名為ASP.NET 5則會給開發者一個錯誤的暗示,開發者會誤認為這只是功能上的升級。而事實是微軟首先寫了一個輕量級跨平台的.NET Core,然后在該平台下重新設計了ASP.NET,新一代的ASP.NET Core 1.0有着眾多新的特性,當然最重要的是實現了跨平台。
值得注意的是從ASP.NET 5到ASP.NET Core 1.0這一命名的轉變會對開發人員造成一些困惑,因為在一些ASP.NET相關的網站中,仍然可以不時地看到Asp.net 5 或者MVC 6等名稱。事實上,由於ASP.NET Core 1.0正式版還沒有發布,命名的改變還在進行當中,整個命名的改變過程到ASP.NET Core1.0正式發布之后才會全部結束。
於此同時也產生了一些新的概念:
DNX:.NET Execution Environment,即.NET運行時環境,在Windows,Mac和Linux下運行.NET應用程序的環境(有點目前windows環境下的.NET Framework的意思),當然這東西是跨平台的,這是跟.NET Framework最大的不同之處。
DNVM:NX Version Manager,即DNX的版本管理工具,利用DNVM可以管理DNX的不同版本,你可以輕松切換到不同的DNX版本中。
.NET Core:可以理解為一個經過精簡的、模塊化的.NET Framework子集,目的是為了跨平台。.NET Core有一系列的類庫組成,叫做"CoreFX",一個更精簡的版本叫做"CoreCLR"。
整個.NET Core所有類庫包括之前提到的ASP.NET Core都是通過Nuget來管理的。
一、安裝ASP.NET Core 1.0
這一過程在Windows、Mac和Linux下各不相同,微軟給出了詳細的安裝文檔,以Windows為例又分為兩種方式,下載安裝或通過命令行安裝。
二、新建ASP.NET Core項目
安裝完畢后,VS2015會增加對應的模板,值得注意的是該模板目前仍舊叫做ASP.NET 5。
三、項目結構
1、project.json文件:
dependencies節點:用來管理Nuget依賴,支持智能提示,這一過程等價於在Nuget package Manager中管理依賴項。
commands節點:在DNX環境下可以使用dnx [command]命令來執行一組命令,對於:
"commands": { "web": "Microsoft.AspNet.Server.Kestrel", "ef": "EntityFramework.Commands" },
可以這樣使用:
dnx web //這一命令將會啟動KestrelHttp 服務器,KestrelHttpServer是微軟基於libuv編寫的跨平台web 服務器 dnx ef
2、frameworks節點:該節點定義了DNX環境
"frameworks": { "dnx451": { }, "dnxcore50": { } },
dnx451表示.NET Framework 4.5.1,dnxcore50表示.NET Core5。
該節點表明此程序可以跑在這兩種DNX環境中,在代碼中還可以通過下面的方式針對具體的環境編寫代碼。
#if DNX451 // utilize resource only available with .NET Framework #endif
四、ASP.NET Core1.0帶來的新特性
1、采用新的文件系統,不再通過工程文件(.sln和.csproj)來定義項目文件清單。
以本Demo為例,所有添加在AspnetCore.Practice文件夾下的文件都會被自動添加在項目中,舉個例子:
打開AspnetCore.Practice\Controllers文件夾:在文件夾內手工添加一個文件ServiceController.cs,並添加如下測試代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Mvc; namespace AspnetCore.Practice.Controllers { public class ServiceController : Controller { public IActionResult Index() { return Content("hello world"); } } }
注意,此時打開VS的項目管理器,可以看到ServiceController已經添加到了項目中。這時若在瀏覽器中直接輸入測試代碼的url,即可看到測試結果。也就是說VS在后台監視到了新文件便會自動添加在項目中並自動完成編譯。
2、Startup類。
該類可以看作整個ASP.NET Core的啟動入口,該類主要存在3個方法:Main函數是入口點,方法ConfigureServices用來向IOC容器中注冊組建,方法Configure則用來注冊Middleware。
也許你第一次見到這個類會有點不明覺厲,該類中3個方法既沒有接口約束,也沒有從父類繼承。微軟在該處采用了這樣一種約定:必須要存在一個名叫Startup的類,同時該類必須要存在上面提到的3個方法,該ASP.NET項目才能順利運行成功。
采用約定而非契約編程的原因在於約定更加靈活。特別是在方法Configure()簽名中,參數可以從容器中注入,意味着你可以定義這樣的Configure方法:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { //... }
也可以定義這樣的Configure方法:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,IUserProvider userProvider) { //... }
要保證這樣的靈活性對於契約編程很難做到。
即便你對約定這一事實一無所知,一些異常信息也會幫助你朝着正確的方向編寫代碼。比如當你將Startup類重命名為其他,例如命名為Bootstrapper,你將會得到如下的提示:
同樣的道理,如果方法ConfigureServices或Configure漏寫也會得到相似的提示。
該類除了采用約定,大部分代碼都是在接口上實現擴展方法的風格,想進一步了解這種代碼風格請閱讀《再談擴展方法,從string.IsNullOrEmpty()說起》。
3、讀取Appsetting
由於已經不再存在web.config文件,所以新的Appsetting也采取了更加通用的設計。在新建項目的時候VS已經幫我們添加了默認的appsettings.json文件。
定義一個鍵值對:"hello": "Hello, world",同時在Startup類的構造函數中將appsettings.json文件添加到了ConfigurationBuilder對象中:
public Startup(IHostingEnvironment env) { // Set up configuration sources. var builder = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true); //… Configuration = builder.Build(); }
同時我們還可以看到一個環境變量env.EnvironmentName,這個設計也有利於我們區分QA,INT,UAT,Production等不同的運行環境。這一變量可以在項目配置中設置:
在代碼中可以通過Configuration["hello"]的方式讀取我們之前定義的Appsettings。
3、默認自帶IOC容器,統一依賴注入API
依賴注入技術從很大程度上使得代碼更加模塊化,會在一定程度上迫使你寫出低耦合,SRP的代碼,另外有着良好設計的代碼也具備更好的可測試性。
ASP.NET Core自己內置了一個非常輕量級的IOC容器,例如以下代碼將組建IEmailSender和ISmsSender分別注冊在了容器中。
public void ConfigureServices(IServiceCollection services) { // Add application services. services.AddTransient<IEmailSender, AuthMessageSender>(); services.AddTransient<ISmsSender, AuthMessageSender>(); }
當然你可以引入第三方比較成熟的IOC容器,項目Dependency Injection定義了一組抽象,只需要將具體的IOC容器實現該抽象即可整合進ASP.NET Core中。就目前的情況來看ASP.NET Core內置的容器比較適合ASP.NET Core內部的組建使用,而實際業務依賴則可以使用第三方更強大的容器來注冊。
另外在ASP.NET Core新的設計中,不光Controller可以進行依賴注入,Filter,View以及ViewModel都可進行注入。這方面的內容比較多,也許會在單獨的文章中進行介紹。
4、Middleware
這一設計借鑒自OWIN katana 項目的管道設計。什么是Middleware?下面這幅圖很好的描述了Middleware是如何在http請求過程中工作的。
在方法Configure中調用的內容都可以理解為Middleware:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { //… app.UseIISPlatformHandler(); //Middleware app.UseStaticFiles(); //Middleware app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); //Middleware }
注冊一個Middleware有兩種寫法,比如你自定義了一個CustomerMiddleware,
第一種注冊方法:
app.UseMiddleware<CustomerMiddleware>();
第二種則更常用:首先寫一個擴展方法
public static class MiddlewareExtensions { public static IApplicationBuilder UseCustomerMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<CustomerMiddleware>(); } }
然后就可以這樣使用了:app.UseCustomerMiddleware();
自定義Middleware請關注后續文章。
5、統一MVC和WebAPI
ASP.NET Core統一了MVC和WebAPI,這表現在這兩者共用同一套代碼,並且在開發過程中不用再繼承各自獨立的Controller基類了。下面展示了如何在同一個Controller中編寫MVC和WebAPI:
public class ServiceController : Controller { private readonly IConfigurationRoot _configurationRoot; public ServiceController(IConfigurationRoot configurationRoot) { _configurationRoot = configurationRoot; } public IActionResult Index() { return Content("hello world"); } public User GetUser() { var user = _configurationRoot.Get<User>("Users");// 讀取appsettings.json中的對象 return user; } }
這一示例采用了構造器注入,為了讓IOC容器注入IConfigurationRoot參數,我們需要將該實例注冊進IOC容器中:
public void ConfigureServices(IServiceCollection services) { //… services.AddInstance(Configuration); }
6、Razor頁面中引入新的Tag Helpers
在之前的Razor頁面中,我們可以利用HtmlHelper擴展來完成數據綁定和頁面展示,例如:
@Html.EditorFor (i => i.Email, new {htmlAttributes = new {@class = "form-control"}})
ASP.NET Core設計了新的方案:
<input asp-for="Email" class="form-control" />
這種寫法更加接近HTML,對純前段人員更加友好,也更利於結合一些前端的MVVM框架來使用。當然我們還可以根據自己的需求編寫自定義的Tag Helpers。使用Tag Helpers需要添加如下Nuget package:
"dependencies": { ... "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-final"}
另外一個例子對比:
HtmlHelpers:
@using (Html.BeginForm( "Register", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { role = "form" })) { @Html.AntiForgeryToken() @Html.ValidationSummary(true, "", new { @class = "text-danger" }) }
TagHelpers:
<form class="form-horizontal" method="post" role="form" asp-controller="Account" asp-action="Register"> <div class="text-danger" asp-validation-summary="ValidationSummary.All"></div> <!-- other tags or tag helpers --> </form>
7、其他一些特性如View Components,Caching…
本文介紹了下一代的ASP.NET版本ASP.NET Core 1.0並且對其新的特性做了概括性的介紹,有助於.NET開發者對ASP.NET Core有一個全面的認識,隨着我對ASP.NET Core的進一步了解還會對一些細節進行更進一步的補充,歡迎大家關注。