《200行代碼,7個對象——讓你了解ASP.NET Core框架的本質》讓很多讀者對ASP.NET Core管道有深刻的理解,知道了ASP.NET Core框架針對每個請求的處理流程。在過去很長一段時間中,有很多人私信給我:能否按照相同的方式分析一下MVC框架。真實的MVC框架其實很復雜,所以我們依然按照類似的方式大刀闊斧地“砍掉”了很多“細枝末節”,利用一個Mini版本的模擬框架將真實ASP.NET Core MVC最核心的部分展示出來。和Mini版本的ASP.NET Core框架一樣,這個Mini版的ASP.NET Core MVC框架同樣采用真實框架一致的設計,並且是同樣可以直接運行的。為了更好的維護,我將這兩個模擬框架放到了github上。
ASP.NET Core Mini: https://github.com/jiangjinnan/AspNetCoreMini
ASP.NET Core MVC Mini:https://github.com/jiangjinnan/AspNetCoreMvcMini
[上篇]路由整合
整個MVC框架建立在路由中間件(《ASP.NET Core 3框架揭秘》下冊具有對路由中間件的專門介紹,本書正在參加京東滿100-50活動,錯過之前5折優惠的同學可以上車了)上。不論是面向Controller的Model-View-Controller編程模型,還是面向頁面的Razor Pages編程模型,每個請求指向的都一個某個Action,所以MVC框架只需要將每個Action封裝成一個路由終結點(RouteEndpoint),並通過自定義的EndpointDataSource注冊到路由中間件上即可。本篇着重關注MVC框架與路由中間件的整合,所以我們將Action方法的定義作了最大的簡化:Action方法都是無參方法,這樣我們就不需要考慮參數綁定的問題;Action方法的返回值都是Task或者Void,所有的請求處理任務都實現在方法中。閱讀全文…
public class FoobarController: Controller { public void Foo(); public Task BarAsync(); public ValueTask BazAsync(); }
[中篇]請求響應
我們在《[上篇]:路由整合》將定義在Controller類型中的Action方法簡化成只返回Task或者Void的方法,並讓方法自身去完成包括對請求予以相應的所有請求處理任務,但真實的MVC框架對Action方法對返回類型沒有任何的限制。一般來說,我們傾向於將Action方法的返回類型定義成IActionResult、Task<IActionResult>或者ValueTask<IActionResult>。如果Action方法返回其他類型的對象,該對象最終還是會被轉換成IActionResult對象。在整個MVC框架針對請求的處理流程中,IActionResult對象主要負責針對請求的響應工作。本篇我們將對上面建立的模擬框架作進一步完善,接觸針對Action方法返回類型的限制。閱讀全文…
public class FoobarController: Controller { public IActionResult Foo(); public Task<IActionResult> BarAsync(); public ValueTask<IActionResult> BazAsync(); public Foobar Qux(); public Task<Foobar> QuuxAsync(); public ValueTask<Foobar> CorgeAsync(); }
[下篇]參數綁定
模擬框架到目前為止都假定Action方法是沒有參數的,我們知道MVC框架對Action方法的參數並沒有作限制,它可以包含任意數量和類型的參數。一旦將“零參數”的假設去除,Action方法的執行就變得沒那么簡單了,因為在執行目標方法之前需要綁定所有的參數。MVC框架采用一種叫做“模型綁定(Model Binding)”的機制來綁定目標Action方法的輸出參數,這可以算是MVC框架針對請求執行流程中最為復雜的一個環節。本篇文章對現有框架做進步完全,通過實現模型綁定接觸上面針對Action方法參數的限制。閱讀全文…
public class FoobarController: Controller { public IActionResult Foo(Foo foo, Bar baz); public Task<IActionResult> BarAsync(Foo foo, Bar baz); public ValueTask<IActionResult> BazAsync(Foo foo, Bar baz); public Foobar Qux(Foo foo, Bar baz); public Task<Foobar> QuuxAsync(Foo foo, Bar baz); public ValueTask<Foobar> CorgeAsync(Foo foo, Bar baz); }