Webform和MVC,為什麼MVC更好一些?
本文由碼農網 – 小峰原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃!
前言
如果你看了最近微軟的議程,你會發現他們現在的焦點除了MVC,還是MVC。問題在於為什麼微軟如此熱衷於丟棄傳統的APS.NET Webform而轉向ASP.NET MVC?本文就主要來討論這個問題。
ASP.NET Webform 後臺程式碼(behind code)—— 福音與詛咒
如果你密切關注過ASP.NET Webform技術,你會發現它更接近視覺化設計,換句話說,開發者只需要從設計皮膚中拖拽控制元件即可完成UI,接著在behind code中實現邏輯程式碼即可完成最後的Web頁面功能。
所以換句話說,當你從設計皮膚中拖拽一個按鈕時,在後臺程式碼中就會生成一個button物件,你只需要在按鈕的點選事件中實現事件響應程式碼即可。
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Developers write code here
}
protected void Button1_Click(object sender, EventArgs e)
{
// Developers write code here
}
}
當我們在頁面中拖拽一些UI元素時,雙擊它們即可在後臺程式碼中生成一系列事件響應程式碼,這些邏輯程式碼都在ASPX.CS檔案中。
這個後臺程式碼檔案是ASP.NET Webform的關鍵,你可以在這個檔案中應用.NET的所以特性,包括事件、委託、HTTP協議以及session等等。
但是這種behind code模式有5個問題,下面我們將一一講述這5個問題,並用MVC的設計思想來分別解決這些問題。
問題1:基於檢視的方案來解決基於行為的需求
我們的網站最終是由使用者使用的,使用者訪問網站肯定會有特定的目的,網站要做的就是通過讓使用者的互動行為來完成其想要的目的。比如當使用者訪問一個購物網站時,也許他的互動行為會是這樣的:
- 購買產品
- 列印發票
這些互動行為是通過按鈕點選、右鍵點選和瀏覽器URL實現的。由於這些互動都是基於HTTP協議的,所以如果我們能將這些互動行為對映到具體的一些方法上,那麼整個架構將會變得簡單很多。
但是微軟做不到這樣,因為它要實現視覺化網頁程式設計,所以他們最終選擇了基於檢視的解決方案。
從上圖可以看出,整個請求過程看上去很奇怪:
- 使用者發起一個HTTP請求,比如HTTP POST / GET
- IIS伺服器將請求對映到檢視
- 檢視呼叫頁面的生命週期,通過事件驅動,呼叫合適的互動方法
- 最後將互動的結果展現給終端使用者
因為微軟一開始就選擇了基於檢視的設計方案,所以架構本身很難向基於使用者互動的設計思想靠攏。換句話說,當使用者發出“購買”請求時,先是訪問了檢視頁面“Shopping.aspx”,後臺邏輯程式碼在“Shopping.aspx.cs”中,頁面生命週期中會將頁面的計算結果返回給使用者。
如果利用MVC的思想,都是基於使用者互動行為的話,那麼請求流程將會是如下所示:
問題2:壞架構的副作用 —— 緊耦合
當你選擇了一個錯誤的架構以後,未來將會出現很多難以解決的副作用,在ASP.NET Webform中就出現了這個問題。儘管behind code後臺程式碼被分離到不同的檔案中,但是ASPX.CS檔案和ASPX檔案卻緊密的聯絡在一起,這將導致系統的耦合度很高,並且很難解耦和,這是一個很頭疼的問題。
簡單地說,我們很難將Customer.aspx.cs和CustomerDetailed.aspx簡單地剝離開,後臺程式碼已經緊緊地將其捆在一起,而且也很難複用。如果我們可以將請求先通過action,而不同過檢視view,action得到的資料再由控制器決定由哪個view展示,那麼請求的流程將會是這樣的:
所以我們可以很方便地控制最終結果是由移動頁面展示還是正常頁面展示,如下程式碼:
public ActionResult Index(string DeviceType)
{
if (viewType == "Mobile")
{
return View("MobileView");
}
else
{
return View("NormalView");
}
}
問題3:HTML不是唯一的返回型別
由於檢視view和後臺程式碼behind code緊密耦合在一起,所以預設的返回型別就固定了,都是HTML型別。如果你想改變型別就必須設定Content-type和呼叫Response.End方法。
如果我們建立一個Action,返回的型別由Action中指定,系統就可以在同一個action中根據不同條件輸出不同的返回型別。程式碼如下:
public ActionResult Index(string viewType)
{
if (viewType == "JSON")
{
return Json(new Customer(), JsonRequestBehavior.AllowGet);
}
else
{
return View("DisplayCustomer", new Customer());
}
}
問題4:檢視和資料的靈活組合
Webform是檢視優先的架構,所以檢視決定了展現的資料,所以檢視的擴充套件性就很差,如果遇到複雜的資料結構,這種方式就顯得力不從心了。
但是如果是行為優先的架構的話,當我們觸發action時,action可以根據不同的請求選擇不同的資料模型和檢視結構,如下圖:
在MVC中,你可以在不同的view中選擇相同的資料模型,比如下面的程式碼,customerdata資料既可以繫結在DetailCustomer檢視中,也可以繫結在Customer檢視中。
public ActionResult Index(string ViewName,Customer customerdata)
{
if (ViewName == "Detailed")
{
return View("DetailCustomer",customerdata);
}
else
{
return View("Customer",customerdata);
}
}
這在Webform中實現起來是非常麻煩的。
問題5、將behind code當做普通的類來進行單元測試
behind code後臺程式碼在Webform中是一個非常龐大的類,並且不能簡單地例項化。要知道Webform是繼承於Page類的,Page類不能直接例項化,因為它有太多的依賴項了。
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
public void Button1_Click(object sender, EventArgs e)
{
Session["SomeSession"] = "Is this set";
}
}
你為什麼想要例項化Page類呢?其中一個原因就是可以方便單元測試。比如我要測試一個按鈕點選事件,用來檢查Session是否設定成功。在Webform中的程式碼看起來不是那麼舒服:
[TestMethod]
public void TestMethod1()
{
WebApplication22.WebForm1 obj = new WebApplication22.WebForm1();
obj.Button1_Click(this, new EventArgs());
}
並且執行時還會丟擲一個異常:
在MVC中,這個類變成了一個普通類,我們可以在測試工程中將它例項化,並對類裡面的屬性方法、Session、viewbag 、 tempdata等進行單元測試。
public class HomeController : Controller // ß this class is simple
{
public ActionResult Index()
{
Session["SomeSession"] = "Is this set";
return View("SomeView");
}
}
所以是否選擇MVC解決方案?
從Webform架構切換到MVC架構,你需要做以下幾件事情:
- 將behind code中的程式碼轉移到controller類中,並將原來的方法轉換成action方法。
- 中間層用資料模型和邏輯介面代替。
- 檢視view只用來展現資料和頁面佈局。
- DAL層和其他層沒有什麼變化,因為它和behind code關係不大。
所以MVC架構中,使用者的請求分為下面3個步驟:
- 終端使用者傳送請求,路由器將請求路由到合適的Controller,Controller是邏輯實體和行為action的集合。
- Controller將請求對映到特定的Action。
- action有兩個任務,第一是獲取合適的資料,第二是將這些資料和檢視view繫結起來。action建立資料模型,並將資料模型連線到指定view,輸出最終的相應結果。
譯文連結:http://www.codeceo.com/article/webforms-vs-mvc.html
英文原文:Webforms vs MVC and Why MVC is better ?
翻譯作者:碼農網 – 小峰
[ 轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]
相關文章
- 為什麼我不再使用MVC框架?MVC框架
- ASP.NET MVC與ASP.NET WebFormASP.NETMVCWebORM
- ASP.Net MVC與WebForm的區別ASP.NETMVCWebORM
- 為什麼要使用MVC+REST+CQRS架構MVCREST架構
- MVC之前的那點事兒系列(10):MVC為什麼不再需要註冊萬用字元(*.*)了?MVC字元
- 為什麼前端模型-檢視-控制器MVC會死?前端模型MVC
- java中 MVC基本思想,優點,和具體指什麼?JavaMVC
- MVC和MVVM,MVPMVCMVVMMVP
- 為什麼iPhone拍照更好看iPhone
- 為什麼有時候spring mvc的interceptor會執行兩次SpringMVC
- 什麼?快來開啟 MVC 的 “擴充 “模式MVC模式
- 使用.NET MVC和BootstrapMVCboot
- MVC 設計模式帶來更好的軟體結構和程式碼重用(轉)MVC設計模式
- 可落地的DDD的(2)-為什麼說MVC工程架構已經過時MVC架構
- WebApi和MVC的區別WebAPIMVC
- MTV和MVC的區別MVC
- ASP.NET MVC和AJAXASP.NETMVC
- 為什麼REST比GraphQL更好? - TomaszJaskuλaREST
- 為什麼軟體測試外包更好?
- spring boot與spring mvc的區別是什麼?Spring BootMVC
- MVC框架MVC框架
- MVC、MVP和MVVM的區別MVCMVPMVVM
- Spring MVC 核心類和介面SpringMVC
- MVC,MVP 和 MVVM 的圖示MVCMVPMVVM
- MVC MVP 和 MVVM 的圖示MVCMVPMVVM
- 教你認清MVC,MVP和MVVMMVCMVPMVVM
- 為什麼SDN和IBN需要更好的網路可見性
- MVC字串處理及MVC@RenderSection小計MVC字串
- [ASP.NET MVC 小牛之路]01 - 理解MVC模式ASP.NETMVC模式
- 從MVC框架看MVC架構的設計MVC框架架構
- MVC,MVP,MVVMMVCMVPMVVM
- MVC——MVP——MVVMMVCMVPMVVM
- mvc架構MVC架構
- MVC模式原理MVC模式
- Spring MVCSpringMVC
- 淺談mvcMVC
- MVC、MVP、MVVMMVCMVPMVVM
- spring - mvcSpringMVC