ASP.NET MVC 5 Web程式設計2 -- URL對映(路由原理)

杭偉發表於2015-02-12

本章將講述ASP.NET MVC5 的路由原理,即URL對映機制。

簡單點就是解釋:為什麼MVC在瀏覽器輸入地址就能訪問到類(或類中的方法)?這是怎麼做到的?我自己可以通過.NET寫出一個自己的MVC框架嗎?

答案是:可以。

模擬URL對映

先來看一個Demo,在傳統的.NET WebForms專案中,實現URL的攔截。

開啟VS2013,新建一個“ASP.NET Web窗體應用程式”專案,並取名為Demo4URLRouting。

為了方便測試,註釋掉Default.aspx頁面的內容和模板引用。這樣做以後,看起來是這樣

然後新建一個ControllerFactory類,實現IHttpHandler介面。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 
 6 namespace Demo4URLRouting
 7 {
 8     /// <summary>
 9     /// 自己寫的ControllerFactory,試圖扮演MVC5中的RouteConfig(路由配置)角色。
10     /// </summary>
11     public class ControllerFactory : IHttpHandler
12     {
13         ControllerFactory()
14         { 
15         }
16 
17         public bool IsReusable
18         {
19             get;
20             set;
21         }
22 
23         public void ProcessRequest(HttpContext context)
24         {
25             context.Response.Write(string.Format("ControllerFactory來攔截請求-> URL為: {0}",context.Request.RawUrl));
26         }
27     }
28 }
View Code

下一步,開啟Web.config配置檔案,在system.webServer節點下新增一項handlers配置

程式碼如下:

<!--for url routing test Start-->
    <handlers>
      <add name="ControllerFactory" verb="*" path="*Account/*" type="Demo4URLRouting.ControllerFactory,Demo4URLRouting" preCondition="integratedMode"/>
    </handlers>
    <!--for url routing test End-->
View Code

此配置的意圖是:攔截站點URL中包含Account關鍵字的全部URL地址。

按F5執行專案,在位址列站點後,輸入account,發現已成功被ControllerFactory類攔截。

繼續,輸入/account/login。發現地址未被攔截。而是跳轉到了預設專案的登陸頁面。

怎麼回事呢?

原來,是專案下的Global.asax檔案中的內容引起,註釋掉RouteConfig註冊的路由。

重新輸入/account/login,發現已成功被ControllerFactory類攔截。

現在我們修改下ControllFactory類中ProcessRequest方法的程式碼,實現URL和類及類中方法的對映。(注意,程式碼做了非常簡單的處理,我們假定忽略大小寫因素,路由格式也和傳統MVC類似)

 1 public void ProcessRequest(HttpContext context)
 2         {
 3             context.Response.Write(string.Format("ControllerFactory來攔截請求-> URL為: {0}",context.Request.RawUrl));
 4             context.Response.Write("<br/>");
 5 
 6             /**
 7              * 將攔截的URL地址分發給對應的Controller
 8              **/
 9             //簡單處理,擷取URL中第一個/和/之間的字串做為要查詢的Controller物件
10             string url = context.Request.RawUrl;
11             string actionURL = url.IndexOf('?') > 0 ? url.Substring(0, url.IndexOf('?')) : url.Substring(0);
12             string[] strArray = actionURL.Split('/');
13             //得到類名稱
14             string targetClassName = (strArray[1] + "Controller");
15             string methodName = string.Empty;
16             if (strArray.Length > 2 && !string.IsNullOrEmpty(strArray[2]))
17                 methodName = strArray[2];//以'/'做分割,類名稱後為方法名稱
18             /**
19              * 從URL獲取類名稱之後,利用反射實現方法的呼叫。
20              **/
21             //獲取Controller例項
22             object instance = Activator.CreateInstance(Type.GetType(string.Format("Demo4URLRouting.Controllers.{0}", targetClassName)));
23             if (instance != null)
24             {
25                 object outputObj = null;//方法輸出內容
26                 if (string.IsNullOrEmpty(methodName) || instance.GetType().GetMethod(methodName) == null)
27                     methodName = "Index";//如果url沒有輸入方法或方法不存在,預設呼叫Index方法
28                 try
29                 {
30                     //呼叫例項方法
31                     outputObj = instance.GetType().GetMethod(methodName).Invoke(instance, null);
32                     context.Response.Write(outputObj);
33                 }
34                 catch(MissingMethodException mme)
35                 {
36                     context.Response.Write(string.Format("<font color='red'>Error! Method not Found!</font>  {0}",mme.Message));
37                 }
38             }
39         }
View Code

在程式碼中,進行了簡單的url分析,獲取類名和方法名,再通過反射機制,實現呼叫。

然後,為了配合測試,我們在解決方案中新建一個Controllers資料夾,在該資料夾下新建一個類AccountController,程式碼如下

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

namespace Demo4URLRouting.Controllers
{
    public class AccountController
    {
        public string Index()
        {
            return "this is the AccountController,<b>Index</b> method.";
        }

        public string HelloWorld()
        {
            return "this is <font color='red'>HelloWorld method</font> which is in the Class named AccountController.";
        }

    }
}
View Code

AccountController類中定義了兩個方法,Index()和HelloWorld()。然後我們通過瀏覽器進行訪問

可以看到,通過在瀏覽器地址中,輸入/Account,實現對AccountController類中Index方法的訪問;輸入/Account/HelloWorld實現對AccountController類中的HelloWorld方法的訪問。

到此,我們初步實現了在WebForms環境下,URL到類的對映。此過程的核心是handlers的配置和利用反射原理呼叫類中的方法。Demo的不足是沒有去消除大小寫問題,沒有更靈活的路由配置等。

那麼問題來了,MVC中這一整套URL對映的機制,是如何實現的呢?

MVC5 URL對映機制

實際上,在寫本篇文章之前,我翻看了大量網上有關MVC的資料,發現大多數對MVC對映機制(也叫MVC路由機制)的描述都非常籠統。

類似不懂裝懂,或者讓你去理解Model-View-Controller的三層,什麼業務分離,或者說了半天說一堆廢話。。。我是深惡痛絕的。如果僅僅是解釋微軟MSDN上能查到的東西?那你的解釋意義在哪裡?不說了,泡沫資訊太多了。

在瞭解MVC URL對映機制這一套原理之前,你首先要了解ASP.NET Routing。 ASP.NET Routing是.NET的一套獨立元件。總的來說,它可以做兩件事情:

1. 將URL請求地址的片段轉交到handler處理;

2. 構造(建立)URL地址。

Routing目前的資訊實在太少,感興趣的可以在MSDN上做初步瞭解。總的來說,Routing做了我們前面模擬做的所有事情,而且毋庸置疑,做的更好更強大。

在MVC中,從URL到Controller,簡要過程大概是這樣:

URL -->  RouteData物件 --> MvcHandler物件 --> IControllerFactory介面 --> Controller 

這個過程很複雜,要詳細闡述其過程,估計要三篇文章以上的篇幅。這裡就不再闡述。

MVC框架中,RouteConfig類中這段程式碼已經做了URL到Controller對映的所有工作。你所需要做的,只是匹配Controller了。

    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 }
            );
        }
    }
View Code

總結

本篇文章重點講述了ASP.NET MVC中的URL對映機制。先是通過在WebForms中模擬URL對映,讓初學者有一個直觀的認識。然後簡要的介紹了MVC中URL對映的機制。由於篇幅所限,在介紹URL對映機制時,只做了簡要的闡述。因為知識量很大,而且建議讀者要有ASP.NET Routing元件的基礎。關於Routing元件,以後有空的話,我再單獨寫文章來講。

在下一篇文章中,我將講述Controller的實際應用及擴充套件,專案中Controller扮演的角色。敬請期待。

本文原始地址

相關文章