ASP.NET的路由系統:URL與物理文件的分離
表現為請求地址與目標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為“*”,則返回所有員工列表
- public class Employee
- {
- public string Id { get; private set; }
- public string Name { get; private set; }
- public string Gender { get; private set; }
- public DateTime BirthDate { get; private set; }
- public string Department { get; private set; }
- public Employee(string id, string name, string gender, DateTime birthDate, string department)
- {
- this.Id = id;
- this.Name = name;
- this.Gender = gender;
- this.BirthDate = birthDate;
- this.Department = department;
- }
- }
- public class EmployeeRepository
- {
- private static IList<Employee> employees;
- static EmployeeRepository()
- {
- employees = new List<Employee>();
- employees.Add(new Employee(Guid.NewGuid().ToString(), "張三", "男",new DateTime(1981, 8, 24), "銷售部"));
- employees.Add(new Employee(Guid.NewGuid().ToString(), "李四", "女",new DateTime(1982, 7, 10), "人事部"));
- employees.Add(new Employee(Guid.NewGuid().ToString(), "王五", "男",new DateTime(1981, 9, 21), "人事部"));
- }
- public IEnumerable<Employee> GetEmployees(string id = "")
- {
- return employees.Where(e => e.Id == id || string.IsNullOrEmpty(id) || id=="*");
- }
- }
對于如上圖所示的兩個頁面實際上對應著同一個.aspx文件,即作為Web應用默認頁面的Default.aspx。要通過一個獨立于物理路徑的URL來訪問該.aspx頁面,我們就需要采用URL路由機制來實現兩者之間的映射。為此我們在添加的Global.asax文件中編寫了如下幾行代碼。如下面的代碼片斷所示,在Application_Start方法中我們通過System.Web.Routing.RouteTable的Routes屬性得到了表示路由對象列表的System.Web.Routing.RouteCollection對象,并調用該列表對象的MapPageRoute方法將Default.aspx頁面(~/Default.aspx)與一個URL模板(employees/{name}/{id)進行了映射。
- public class Global : System.Web.HttpApplication
- {
- protected void Application_Start(object sender, EventArgs e)
- {
- var defaults = new RouteValueDictionary{{"name","*"},{"id","*"}};
- RouteTable.Routes.MapPageRoute("", "employees/{name}/{id}", "~/Default.aspx", true,defaults);
- }
- }
作為MapPageRoute方法***一個參數的RouteValueDictionary對象用于指定定義在路由模板中相應變量({name}和{id})的默認值。對于指定了默認值的路由對象,在當前請求地址的后續部分缺失的情況下,它會采用提供的默認值對該地址進行填充之后再進行模式的匹配。在如上所示的代碼片斷中,我們將{name}和{id}兩變量的默認值均指定為“*”。對于針對URI為http://localhost:2738/employees的請求,我們注冊的路由對象會將其格式成http://localhost:2738/employees/*/*,后者無疑是和定義的URL模式變現出來的模式是匹配的。
在Default.aspx頁面中,我們分別采用GridView和DetailsView來顯示所有員工列表和某個列表的詳細信息,下面的代碼片斷表示該頁面主體部分的HTML。值得一提的是:GridView模板中顯示為員工姓名的HyperLinkField的連接采用了上面我們定義在URL模板(employees/{name}/{id))中的模式。
- <form id="form1" runat="server">
- <div id="page">
- <asp:GridView ID="GridViewEmployees" runat="server" AutoGenerateColumns="false" Width="100%">
- <Columns>
- <asp:HyperLinkField HeaderText="姓名" DataTextField="Name" DataNavigateUrlFields="Name,Id" DataNavigateUrlFormatString="~/employees/{0}/{1}" />
- <asp:BoundField DataField="Gender" HeaderText="性別" />
- <asp:BoundField DataField="BirthDate" HeaderText="出生日期" DataFormatString="{0:dd/MM/yyyy}" />
- <asp:BoundField DataField="Department" HeaderText="部門" />
- </Columns>
- </asp:GridView>
- <asp:DetailsView ID="DetailsViewEmployee" runat="server" AutoGenerateRows="false" Width="100%">
- <Fields>
- <asp:BoundField DataField="ID" HeaderText= "ID" />
- <asp:BoundField DataField="Name" HeaderText= "姓名" />
- <asp:BoundField DataField="Gender" HeaderText="性別" />
- <asp:BoundField DataField="BirthDate" HeaderText="出生日期" DataFormatString="{0:dd/MM/yyyy}" />
- <asp:BoundField DataField="Department" HeaderText="部門" />
- </Fields>
- </asp:DetailsView>
- </div>
- </form>
Default.aspx頁面的整個后臺代碼定義如下。由于所有員工列表和單一員工的詳細信息均體現在該頁面中,所以我們需要根據其請求地址來判斷應該呈現怎樣的數據,而這可以通過RouteData屬性表示的路由數據來實現。Page具有一個類型為System.Web.Routing.RouteData的RouteData表示通過注冊的與當前請求匹配的路由對象對請求地址進行解析生成的路由數據。RouteData的Values屬性是一個存儲路由變量的字典,其Key為變量名稱。在如下所示的代碼片斷中,我們得到表示員工ID的路由變量(RouteData.Values["id"]),如果它是默認值則表示當前請求是針對員工列表的,反之則是這對指定的某個具體員工的。
- public partial class Default : Page
- {
- private EmployeeRepository repository;
- public EmployeeRepository Repository
- {
- get { return null == repository ? repository = new EmployeeRepository() : repository; }
- }
- protected void Page_Load(object sender, EventArgs e)
- {
- if (this.IsPostBack)
- {
- return;
- }
- string employeeId = this.RouteData.Values["id"] as string;
- if (employeeId == "*" || string.IsNullOrEmpty(employeeId))
- {
- this.GridViewEmployees.DataSource = this.Repository.GetEmployees();
- this.GridViewEmployees.DataBind();
- this.DetailsViewEmployee.Visible = false;
- }
- else
- {
- var employees = this.Repository.GetEmployees(employeeId);
- this.DetailsViewEmployee.DataSource = employees;
- this.DetailsViewEmployee.DataBind();
- this.GridViewEmployees.Visible = false;
- }
- }
- }
原文鏈接:http://www.cnblogs.com/artech/archive/2012/03/19/aspnet-routing-01.html
【編輯推薦】