ASP.NET MVC第004天_路由的觀念解釋

 .NET MVC中路由一詞源自於路由器概念
主要負責網路中數據的傳輸路徑安排

在.net mvc中路由系統則是將各種URL請由轉交給控制器來進行安排處理
https://www.c-sharpcorner.com/UploadFile/ff2f08/routing-in-Asp-Net-mvc/



用戶從Client端瀏覽器發起URL請求後背後透過MVC路由機制查找路由表
去找尋到對應的Controller做後續相應Action處理



https://www.dotnetodyssey.com/asp-net-mvc-5-free-course/how-web-works-asp-net-mvc-fits-web-application-development/


.NET MVC 路由的一些重要叮嚀
1.路由名稱一定要是唯一不可重複,會報錯。
2.若有一組URL能在多個路由匹配時,預設會採用第一個匹配原則。
3.URL模式不允許連續的URL參數(大括號包起來)


Q1.如何自訂路由?
Ans:基本上要先回顧到我們怎麼透過MapRoute方法去定義路由匹配,
在該方法中主要參數組成如下圖表示,若是
不同的路由名稱(Route Name)即代表不同組的路由。
(PS:路由名稱一定要是唯一不可重複,會報錯。)
在route機制中controller 跟 action 屬於保留字
而自訂其他路由基本上就是同樣的call MapRoute然後更改name即可




預設空的MVC專案會產生這組路由
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

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

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

換言之我今天捨麼都不指定時,預設載入到首頁
名為Default的路由意思就是指今天捨麼都不設定的情況下默認
首頁網址 : /
對應的Route Url pattern : {controller}/{action}/{id}
和defaults : 參數默認值(實質指引到對應的URL參數)
也就是去名為Home的控制器下trigger 名為Index的動作來返回我們要的View
id不像controller跟action都指派到一個很明確的目標
預設可有可無因此設值為UrlParameter.Optional
(若不是設定UrlParameter.Optional則會變成必帶參數)


這裡若新增額外的控制器對應View並想自訂額外路由
假如有一個網頁是專門用來呈現訂單
新增一個對應的Orders Controller

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

namespace MVC_RoutingTest.Controllers
{
    public class OrdersController : Controller
    {
        // GET: Orders
        public ActionResult Index()
        {
            return View();
        }

        public string List()
        {
            return "Order List";
        }
    }
}


相應Orders的MapRoute

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

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

            routes.MapRoute(
                name: "Orders",
                url: "Orders/{action}/{id}",
                defaults: new { controller = "Orders", action = "List", id = UrlParameter.Optional }
            );

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}




預設的路由是採用URL規則
也就是URL中參數形式為: {controller}/{action}/{id}
通常是一組大括號裡面包含一組字串或字元的占位符屬於可變的占位符(即變數)
和相應一些固定不可變的字符又稱字面量 (可能類似 斜線 '/' )組合成



URL格式規則中要特別注意
1.不可以用 '/' 或 '~' 來作為開頭
(X) /{controller}
(X) ~products/detail{id}

2.不可以包含 '?'
(X) {action}?id={id}

3.不可以連續占位符
(X) {controller}{action}/{id}


可看到一些實際案例(左邊為URL模式/右邊為有符合的URL例子)

也注意 URL模式中是不區分大小寫的!!!
另外不在大括號中的常量通常也是固定不會改變的



URL模式匹配原理上就是一種對號入座的概念
基本上只要符合常量部分都一樣且URL占位符搭配的模式有符合基本上就能匹配的到
對應頁面




Q2.id怎麼傳遞的?要怎麼限定傳遞格式只能是數值或是英文字母?
這裡沿用Default路由mapping
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

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

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}


改寫controller中Index方法的回傳Content (一串字串覆寫調目前頁面資訊)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MVC_RoutingTest.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index(string id)
        {
            string request_id = id;
            string output = string.Format("ID:{0}",request_id);
            return Content(output);
            //return View();
        }
    }
}



可以看到運行後ID有透過url 傳遞並呈現出來
若要限定格式則可以透過路由約束
多串參數給defaults 參數串



這裡用到的overload


routes.MapRoute(
     name: "Default",
     url: "{controller}/{action}/{id}",
     defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ,
     //constraints: new { id = @"\d+" } //限定一個或一個以上的數字字元
     constraints: new { id = @"[a-zA-Z]+" } //限定一個或一個以上的英文字元
);




Q3.可否改default產生的範例,省略action直接只輸入{controller}/{id}?
Ans:可以

範例程式
在Default 路由Mapping之前(上) 定義另一組路由Mapping規則

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

namespace MVC_RoutingTest
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
               name: "TestNoAction",
               url: "{controller}/{id}",
               defaults: new { controller = "Home", action = "Index" }
           );

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}
(注意!! 一定要在之前,不然輸入ID時候會被認定為找不到對應action而報404錯誤)





我們希望開放
呼叫端輸入URL模式
可以是{controller}/{action}/{id}
或簡化為{controller}/{id}

都能在畫面秀傳送的ID值
這裡偷偷先用一下.NET MVC  其中一種從Controller pass 資料給View的方式ViewBag


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

namespace MVC_RoutingTest.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index(string id)
        {
            string request_id = id;
            ViewBag.reqID = id;
            return View();
        }

    }
}

Index.cshtml

@{
    Layout = null;   
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        <h1>Home View</h1>
        <h1>ID = @ViewBag.reqID </h1>
    </div>
</body>
</html>

運行結果
第一次開起來
URL為localhost待配一組隨機vs分配的port


http://localhost:55903/
也等同於直接輸入
http://localhost:55903/Home

接著再輸入下一層action
http://localhost:55903/Home/Index
會發現怪現象ID直接傳入目前的Action名稱 Index
原因是預設剛開起來沒有ID
但符合一開始定義的路由網址pattern
{controller}/{id}
所以被認定成id了


這裡沿用上述學到的路由約束搭配正規表示法 : (?!regex)
修改RouteConfig.cs
限制ID不包含 Index 這個action名稱

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

namespace MVC_RoutingTest
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
               name: "TestNoAction",
               url: "{controller}/{id}",
               defaults: new { controller = "Home", action = "Index" },
               constraints: new { id = @"(?!Index).*" }
           );
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

這樣就達到了符合這兩種傳遞ID的pattern
可以是{controller}/{action}/{id}
也可是{controller}/{id}

http://localhost:55903/Home/Index/1000
http://localhost:55903/Home/1000






Q4.如果對一URL而言,有多種URL路由模式都能匹配會怎麼解析呢?
Ans:多個路由匹配規則:若有一組URL能在多個路由匹配時,預設會採用第一個匹配原則。


這裡寫個測試
我們有兩組控制器


在HomeController Index動作返回的View中加上一個link tag
導向網址輸入 "/Home/Index/1000"
-->符合{controller}/{action}/{id}這個URL模式


我們RouteConfig中程式
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

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



            //測試多路由時的執行優先順序
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );


            routes.MapRoute(
               name: "Test1",
               url: "{first}/{second}/{third}",
               defaults: new { controller = "Work", action = "Index", id = UrlParameter.Optional }
           );

            
        }
    }
}
會優先執行到HomeController中的Action回傳View




但當我們調換兩個MapRoute 調用的順序
Default的路由移到下面使第一個變成Test1路由

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

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



            //測試多路由時的執行優先順序            
            routes.MapRoute(
               name: "Test1",
               url: "{first}/{second}/{third}",
               defaults: new { controller = "Work", action = "Index", id = UrlParameter.Optional }
           );

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}


此時若再次點link一樣操作會跳轉到Work 的View


因為URL模式也符合
Test1路由定義的{first}/{second}/{third}






















Ref:
建立自訂路由 (C#)

建立路由條件約束 (C#)

ASP.NET MVC 開發心得分享 (21):Routing 觀念與技巧

【MVC教學】3. Route(一)

[探索 5 分鐘] 淺談 ASP.NET MVC 路由 (routing)


In ASP.Net, can we map multiple URLs to the same action.

ASP.NET MVC Route 自訂限制條件(constraints)的技巧

留言

這個網誌中的熱門文章

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

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

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