表現為請求地址與目標Controller和Action的動態映射的URL路由系統並不是專屬於ASP.NET MVC,而是直接建立在ASP.NET 中。ASP.NET通過URL路由系統實現了請求地址與物理文件的分離。
一、URL與物理文件的分離
對於一個 ASP.NET Web Form應用來說,任何一個請求都對應著某個具體的物理文件。部署在Web服務器上的物理文件可以是靜態的(比如圖片和靜態HTML文件等),也可以是動態的(比如.asxp文件)。對於靜態文件的請求,ASP.NET直接返回文件的整個內容;而針對動態文件的請求則會觸發相關代碼的執行,並最終返回執行後的結果。但是這種將URL與物理文件緊密綁定在一起的方式並不是一種好的解決方案,它帶來的局限性主要體現在如下幾個方面:
靈活性:由於URL是對物理文件路徑的反映,意味著如果物理文件的路徑發生了改變(比如改變了文件的目錄結構或者文件名),原來基於該文件鏈接將變得無效。
可讀性:在很多情況下,URL不僅僅需要能夠訪問正確的網絡資源,還需要具有很好的可讀性,最好的URL應該讓我們一眼就能看出針對它訪問的目標資源是什麼。請求地址與物理文件緊密綁定讓我們完全失去了定義高可讀性URL的機會。
SEO優化:對於網站開發來說,為了迎合搜索引擎檢索的規則,我們需要對URL進行有效的設計使之能易於被主流的引擎檢索收錄。如果URL完全與物理地址關聯,這無異於失去了SEO優化的能力。
出於針對URL與物理文件綁定機制帶來的上述局限,我們需要一種更加靈活的機制實現針對物理文件的請求地址與文件本身的路徑的分離,通過一種動態映射的機制實現URL與物理文件的關聯。
說到這裡,可能很多人會想到URL重寫。為了使Web應用可以獨立地涉及用於訪問應用資源的URL,微軟為IIS 7編寫了一個URL重寫模塊。這是一個基於規則的URL重寫引擎,用於在URL被Web服務器處理之前改變請求的URL。對於動態Web應用程序,它可以為用戶和搜索引擎提供友好的URL,URL重寫和重定向是基於HTTP頭和服務器變量的,並可以對站點內容進行訪問控制。
URL重寫在IIS級別解決了URL與物理地址的分離,它通過一個基於本地(Native)代碼的模塊注冊到IIS進行HTTP請求處理的管道上,所以可以應用於所以寄宿於IIS中的Web應用。而URL路由系統則是ASP.NET的一部分,是通過托管代碼實現的。為了讓讀者對ASP.NET的URL路由具有一個感官的認識,我們來演示一個簡單的實例。
二、 實例演示:通過URL路由實現請求地址與.aspx頁面的映射
接下來我們將創建一個簡單的ASP.NET Web Forms應用,並采用一個獨立於.aspx文件路徑的URL來訪問對應的Web頁面,而兩者之間的映射通過URL路由來實現。我們是一個關於員工管理的場景,我們將創建一個頁面來顯示員工的列表和某個員工的詳細信息,頁面呈現出來效果如下圖所示。
我們將關注點放到上圖所示的兩個頁面的URL上。用於顯示員工列表的頁面地址為http://localhost:2738/employees。當用戶點擊某個顯示為姓名的連接後,用於顯示所選員工詳細信息的頁面被呈現出現,其頁面地址的URL模式為http://localhost:2738/employees/{姓名}/{ID}。對於後者,最終用戶一眼可以從URL中看出通過該地址獲取的是哪個員工的信息。有人可能會問,為什麼我們要在URL同時包含員工的姓名和ID呢?這是因為ID(本例采用GUID)的可讀性不如員工姓名,但是員工姓名不具有唯一性,在這裡我們使用的ID是為了邏輯處理的需要而提供的唯一標識,而姓名則是出於可讀性的需要。
我們將員工的所有 信息(ID、姓名、性別、出生日期和所在部門)定義在如下所示的Employee類型中。我們照例定義了如下一個EmployeeRepository類型表示維護員工列表的領域模型。維護的員工列表通過靜態字段employees 表示。EmployeeRepository的GetEmployees方法根據指定的ID返回指包含相應員工的列表,如果指定的ID為“*”,則返回所有員工列表
1: public class Employee
2: {
3: public string Id { get; private set; }
4: public string Name { get; private set; }
5: public string Gender { get; private set; }
6: public DateTime BirthDate { get; private set; }
7: public string Department { get; private set; }
8:
9: public Employee(string id, string name, string gender, DateTime birthDate, string department)
10: {
11: this.Id = id;
12: this.Name = name;
13: this.Gender = gender;
14: this.BirthDate = birthDate;
15: this.Department = department;
16: }
17: }
18: public class EmployeeRepository
19: {
20: private static IList<Employee> employees;
21: static EmployeeRepository()
22: {
23: employees = new List<Employee>();
24: employees.Add(new Employee(Guid.NewGuid().ToString(), "張三", "男",new DateTime(1981, 8, 24), "銷售部"));
25: employees.Add(new Employee(Guid.NewGuid().ToString(), "李四", "女",new DateTime(1982, 7, 10), "人事部"));
26: employees.Add(new Employee(Guid.NewGuid().ToString(), "王五", "男",new DateTime(1981, 9, 21), "人事部"));
27: }
28: public IEnumerable<Employee> GetEmployees(string id = "")
29: {
30: return employees.Where(e => e.Id == id || string.IsNullOrEmpty(id) || id=="*");
31: }
32: }