ASP.NET MVC第001天_Webform跟MVC差在哪?_MVC專案架構概述

在很久以前有用Java swing來模擬實作MVC的概念
https://coolmandiary.blogspot.com/2019/01/java-mvcmvcjava-gui.html

也有在Java EE中淺淺地帶過MVC的概念
https://coolmandiary.blogspot.com/2018/03/day1j2eejava-2-enterprise.html


今天要來分享一下網頁上開發透過.NET MVC的實作初體驗

在以前的web form 強調事件驅動跟Page Life Cycle和一些狀態儲存的觀念
而.NET MVC跟Webform的差別
我們可用下面的圖呈現



Weborm中有區分所謂屬於form的跟非form的元素
Webform中由於有肥大的ViewState內容,使得在傳送網頁過程網頁資料量比較大
Webform控件雖然一開始是為了節省開發時間而封裝但也使得對於html控制不夠直覺
不太容易撰寫單元測試(不容易導入TDD)

相較於WebForm的上述缺點 MVC帶來的是

1.淺顯的關注點分離SoC(Separation of Concerns),開發人員只需要關注目前物件上一次性解決
不會受到其他物件的干擾或連動影響

2.易於測試及分工,由於江M、V、C職責拆分出來所以網頁設計人員和前端都可以直接參與View層設計,但實際上可能較多仍是一個人去負責View跟後端Model , Controller的整合。
在設計上保持的原則就是View愈單純愈笨愈好不將業務邏輯寫於此

3.HTTP的輸出內容較有完全的控制權(寫的風格有點像Classic ASP)

4.更容易導入IoC (Inversion of Control)或 DI (Dependency Injection)

從Webform轉移到MVC必須捨棄的特性

1.ViewState
2.頁面事件模型(Event Model)
3.封裝的控制項
4.頁面追蹤機制(Page Trace)

以上這些於.net mvc都不太支援了
但其餘的asp.net特性基本上仍保留
Application Life Cycle , Web Caching , Session , Authentication 等等


MVC職責再次說明



Model(模型):即定義資料型態和DB溝通、保存程式狀態並執行相應業務邏輯的地方
絕大多數時候可能會是用DB中某Table的某一Record Entity各項Column Data Type或是

View(檢視):網頁畫面(html , 和一些js 、css)

Controller(控制器):判斷及請求某個Model、判斷和導向某個View
Controller會接受Routing分析出的路由,也負責和Model傳遞資料輸出到View。


建立一Webform範例專案跟MVC範例專案
進行比較

Webform


從首頁、關於這兩個網頁檔案
就可以明確觀察到以前的Webform遵循的是「網址路徑」即「檔案路徑」
首頁
http://localhost:12081/Default
http://localhost:12081/
預設都是對應網站根目錄下的
/Default.aspx
/Default.aspx.cs


關於
http://localhost:12081/About
/About.aspx
/About.aspx.cs


但到了MVC則是必須透過asp.net mvc的架構中的網址路由(Routing)來找尋檔案的
其定義於Global.asax.cs檔案中





首頁
http://localhost:12047/
對應程式位置
/Views/Home/Index.aspx
/Controller/HomeController.cs


在Global.asax.cs檔案中

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace WebApp_Template
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}

可以看到間接調用到RouteConfig去設置網址路由
在RouteConfig.cs檔案中

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace WebApp_Template
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",  //路由名稱
                url: "{controller}/{action}/{id}", //URL和參數
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

可以清楚看到明確預設load進來就指定導向首頁的View
url: "{controller}/{action}/{id}"
此句是用來定義網址路徑如何對應MVC參數的

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
IgnoreRoute
主要是去設定*.axd格式網址路徑不透過asp.net MVC方式執行
(Ex:內建的Trace.axd 或預設的HttpHandler)


routes.MapRoute(
  name: "Default",  //路由名稱
  url: "{controller}/{action}/{id}", //URL和參數
  defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
MapRoute方法定義三參數
1.路由名稱
2.網址路徑要如何對應到控制器、動作和路由值設定串
在定義Routing時看到的大括號包覆的一變數名稱也就是所謂的路由變數!
當中必要的{controller}、{action}
若沒有設定預設值就會使MVC無法正常運作
此兩核心變數會傳給System.Web.Mvc.MvcHander class接著交給DefaultControllerFactory進行創建
其餘路由變數則可自訂

以About此範例來看就可得知
{controller}代表Home
{action}代表About

因此.net mvc底層機制會先到Controllers的folder下
找到Home 這個Controller (HomeController.cs)
再到此Controller內的About 這個publc method 也就是此action (網頁主程式進入點)了

HomeController.cs檔案內容
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace WebApp_Template.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View();
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
    }
}
3.控制器、動作跟其他路由值的預設設定內容
若只輸入預設的本機localhost url
http://localhost/
則會透過Routing比對後得知網頁目的位址不存在
而採用default的首頁設定,也就是Home

網頁就會從Controllers目錄去尋找Home 控制器的程式(HomeController.cs)
去找public 的Index method,該方法就是對應的action對應作為網頁入口。











在MVC專案下
一個Action(ActionResult)會對應一個View
以HomeController為例

就有Index , About , Contact三個Action

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace WebApp_Template.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View();
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
    }
}


各自對應到的三個View
在Views/Home/  下



那預設如果是新手通常觀察就知道
Views就是一些前端網頁
然後就習以為然的在下方新增網頁了

這裡右鍵->加入->檢視(V)


輸入好名字加入

MyWeb 的View預設程式
@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>MyWeb</title>
</head>
<body>
    <div> 
    </div>
</body>
</html>

就產生了



但直接執行狀況就會是報錯

主要是因為MVC設計上是不允許你直接瀏覽View
而是透過Controller先訪問後由Action來指定到哪






這裡資源找不到就是因為Controller沒定義導向MyWeb 這個View的Action
於Controller補上程式後就可以正常顯示了


所以在MVC中一律要先有一Controller去主導做某一Action才能順利指向View







留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header