ASP.NET的路由系統(tǒng):URL與物理文件的分離
表現(xiàn)為請求地址與目標(biāo)Controller和Action的動態(tài)映射的URL路由系統(tǒng)并不是專屬于ASP.NET MVC,而是直接建立在ASP.NET 中。ASP.NET通過URL路由系統(tǒng)實(shí)現(xiàn)了請求地址與物理文件的分離。[源代碼地址從這里下載]
一、URL與物理文件的分離
對于一個(gè) ASP.NET Web Form應(yīng)用來說,任何一個(gè)請求都對應(yīng)著某個(gè)具體的物理文件。部署在Web服務(wù)器上的物理文件可以是靜態(tài)的(比如圖片和靜態(tài)HTML文件等),也可以是動態(tài)的(比如.asxp文件)。對于靜態(tài)文件的請求,ASP.NET直接返回文件的整個(gè)內(nèi)容;而針對動態(tài)文件的請求則會觸發(fā)相關(guān)代碼的執(zhí)行,并最終返回執(zhí)行后的結(jié)果。但是這種將URL與物理文件緊密綁定在一起的方式并不是一種好的解決方案,它帶來的局限性主要體現(xiàn)在如下幾個(gè)方面:
- 靈活性:由于URL是對物理文件路徑的反映,意味著如果物理文件的路徑發(fā)生了改變(比如改變了文件的目錄結(jié)構(gòu)或者文件名),原來基于該文件鏈接將變得無效。
- 可讀性:在很多情況下,URL不僅僅需要能夠訪問正確的網(wǎng)絡(luò)資源,還需要具有很好的可讀性,最好的URL應(yīng)該讓我們一眼就能看出針對它訪問的目標(biāo)資源是什么。請求地址與物理文件緊密綁定讓我們完全失去了定義高可讀性URL的機(jī)會。
- SEO優(yōu)化:對于網(wǎng)站開發(fā)來說,為了迎合搜索引擎檢索的規(guī)則,我們需要對URL進(jìn)行有效的設(shè)計(jì)使之能易于被主流的引擎檢索收錄。如果URL完全與物理地址關(guān)聯(lián),這無異于失去了SEO優(yōu)化的能力。
出于針對URL與物理文件綁定機(jī)制帶來的上述局限,我們需要一種更加靈活的機(jī)制實(shí)現(xiàn)針對物理文件的請求地址與文件本身的路徑的分離,通過一種動態(tài)映射的機(jī)制實(shí)現(xiàn)URL與物理文件的關(guān)聯(lián)。
說到這里,可能很多人會想到URL重寫。為了使Web應(yīng)用可以獨(dú)立地涉及用于訪問應(yīng)用資源的URL,微軟為IIS 7編寫了一個(gè)URL重寫模塊。這是一個(gè)基于規(guī)則的URL重寫引擎,用于在URL被Web服務(wù)器處理之前改變請求的URL。對于動態(tài)Web應(yīng)用程序,它可以為用戶和搜索引擎提供友好的URL,URL重寫和重定向是基于HTTP頭和服務(wù)器變量的,并可以對站點(diǎn)內(nèi)容進(jìn)行訪問控制。
URL重寫在IIS級別解決了URL與物理地址的分離,它通過一個(gè)基于本地(Native)代碼的模塊注冊到IIS進(jìn)行HTTP請求處理的管道上,所以可以應(yīng)用于所以寄宿于IIS中的Web應(yīng)用。而URL路由系統(tǒng)則是ASP.NET的一部分,是通過托管代碼實(shí)現(xiàn)的。為了讓讀者對ASP.NET的URL路由具有一個(gè)感官的認(rèn)識,我們來演示一個(gè)簡單的實(shí)例。
二、 實(shí)例演示:通過URL路由實(shí)現(xiàn)請求地址與.aspx頁面的映射
接下來我們將創(chuàng)建一個(gè)簡單的ASP.NET Web Forms應(yīng)用,并采用一個(gè)獨(dú)立于.aspx文件路徑的URL來訪問對應(yīng)的Web頁面,而兩者之間的映射通過URL路由來實(shí)現(xiàn)。我們是一個(gè)關(guān)于員工管理的場景,我們將創(chuàng)建一個(gè)頁面來顯示員工的列表和某個(gè)員工的詳細(xì)信息,頁面呈現(xiàn)出來效果如下圖所示。
我們將關(guān)注點(diǎn)放到上圖所示的兩個(gè)頁面的URL上。用于顯示員工列表的頁面地址為http://localhost:2738/employees。當(dāng)用戶點(diǎn)擊某個(gè)顯示為姓名的連接后,用于顯示所選員工詳細(xì)信息的頁面被呈現(xiàn)出現(xiàn),其頁面地址的URL模式為http://localhost:2738/employees/{姓名}/{ID}。對于后者,最終用戶一眼可以從URL中看出通過該地址獲取的是哪個(gè)員工的信息。有人可能會問,為什么我們要在URL同時(shí)包含員工的姓名和ID呢?這是因?yàn)镮D(本例采用GUID)的可讀性不如員工姓名,但是員工姓名不具有唯一性,在這里我們使用的ID是為了邏輯處理的需要而提供的唯一標(biāo)識,而姓名則是出于可讀性的需要。
我們將員工的所有 信息(ID、姓名、性別、出生日期和所在部門)定義在如下所示的Employee類型中。我們照例定義了如下一個(gè)EmployeeRepository類型表示維護(hù)員工列表的領(lǐng)域模型。維護(hù)的員工列表通過靜態(tài)字段employees 表示。EmployeeRepository的GetEmployees方法根據(jù)指定的ID返回指包含相應(yīng)員工的列表,如果指定的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: }
對于如上圖所示的兩個(gè)頁面實(shí)際上對應(yīng)著同一個(gè).aspx文件,即作為Web應(yīng)用默認(rèn)頁面的Default.aspx。要通過一個(gè)獨(dú)立于物理路徑的URL來訪問該.aspx頁面,我們就需要采用URL路由機(jī)制來實(shí)現(xiàn)兩者之間的映射。為此我們在添加的Global.asax文件中編寫了如下幾行代碼。如下面的代碼片斷所示,在Application_Start方法中我們通過System.Web.Routing.RouteTable的Routes屬性得到了表示路由對象列表的System.Web.Routing.RouteCollection對象,并調(diào)用該列表對象的MapPageRoute方法將Default.aspx頁面(~/Default.aspx)與一個(gè)URL模板(employees/{name}/{id)進(jìn)行了映射。
1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: var defaults = new RouteValueDictionary{{"name","*"},{"id","*"}};
6: RouteTable.Routes.MapPageRoute("", "employees/{name}/{id}", "~/Default.aspx", true,defaults);
7: }
8: }
作為MapPageRoute方法最后一個(gè)參數(shù)的RouteValueDictionary對象用于指定定義在路由模板中相應(yīng)變量({name}和{id})的默認(rèn)值。對于指定了默認(rèn)值的路由對象,在當(dāng)前請求地址的后續(xù)部分缺失的情況下,它會采用提供的默認(rèn)值對該地址進(jìn)行填充之后再進(jìn)行模式的匹配。在如上所示的代碼片斷中,我們將{name}和{id}兩變量的默認(rèn)值均指定為“*”。對于針對URI為http://localhost:2738/employees的請求,我們注冊的路由對象會將其格式成http://localhost:2738/employees/*/*,后者無疑是和定義的URL模式變現(xiàn)出來的模式是匹配的。
在Default.aspx頁面中,我們分別采用GridView和DetailsView來顯示所有員工列表和某個(gè)列表的詳細(xì)信息,下面的代碼片斷表示該頁面主體部分的HTML。值得一提的是:GridView模板中顯示為員工姓名的HyperLinkField的連接采用了上面我們定義在URL模板(employees/{name}/{id))中的模式。
1: <form id="form1" runat="server">
2: <div id="page">
3: <asp:GridView ID="GridViewEmployees" runat="server" AutoGenerateColumns="false" Width="100%">
4: <Columns>
5: <asp:HyperLinkField HeaderText="姓名" DataTextField="Name" DataNavigateUrlFields="Name,Id" DataNavigateUrlFormatString="~/employees/{0}/{1}" />
6: <asp:BoundField DataField="Gender" HeaderText="性別" />
7: <asp:BoundField DataField="BirthDate" HeaderText="出生日期" DataFormatString="{0:dd/MM/yyyy}" />
8: <asp:BoundField DataField="Department" HeaderText="部門" />
9: </Columns>
10: </asp:GridView>
11: <asp:DetailsView ID="DetailsViewEmployee" runat="server" AutoGenerateRows="false" Width="100%">
12: <Fields>
13: <asp:BoundField DataField="ID" HeaderText= "ID" />
14: <asp:BoundField DataField="Name" HeaderText= "姓名" />
15: <asp:BoundField DataField="Gender" HeaderText="性別" />
16: <asp:BoundField DataField="BirthDate" HeaderText="出生日期" DataFormatString="{0:dd/MM/yyyy}" />
17: <asp:BoundField DataField="Department" HeaderText="部門" />
18: </Fields>
19: </asp:DetailsView>
20: </div>
21: </form>
Default.aspx頁面的整個(gè)后臺代碼定義如下。由于所有員工列表和單一員工的詳細(xì)信息均體現(xiàn)在該頁面中,所以我們需要根據(jù)其請求地址來判斷應(yīng)該呈現(xiàn)怎樣的數(shù)據(jù),而這可以通過RouteData屬性表示的路由數(shù)據(jù)來實(shí)現(xiàn)。Page具有一個(gè)類型為System.Web.Routing.RouteData的RouteData表示通過注冊的與當(dāng)前請求匹配的路由對象對請求地址進(jìn)行解析生成的路由數(shù)據(jù)。RouteData的Values屬性是一個(gè)存儲路由變量的字典,其Key為變量名稱。在如下所示的代碼片斷中,我們得到表示員工ID的路由變量(RouteData.Values["id"]),如果它是默認(rèn)值則表示當(dāng)前請求是針對員工列表的,反之則是這對指定的某個(gè)具體員工的。
1: public partial class Default : Page
2: {
3: private EmployeeRepository repository;
4: public EmployeeRepository Repository
5: {
6: get { return null == repository ? repository = new EmployeeRepository() : repository; }
7: }
8: protected void Page_Load(object sender, EventArgs e)
9: {
10: if (this.IsPostBack)
11: {
12: return;
13: }
14: string employeeId = this.RouteData.Values["id"] as string;
15: if (employeeId == "*" || string.IsNullOrEmpty(employeeId))
16: {
17: this.GridViewEmployees.DataSource = this.Repository.GetEmployees();
18: this.GridViewEmployees.DataBind();
19: this.DetailsViewEmployee.Visible = false;
20: }
21: else
22: {
23: var employees = this.Repository.GetEmployees(employeeId);
24: this.DetailsViewEmployee.DataSource = employees;
25: this.DetailsViewEmployee.DataBind();
26: this.GridViewEmployees.Visible = false;
27: }
28: }
29: }
ASP.NET的路由系統(tǒng):URL與物理文件的分離
ASP.NET的路由系統(tǒng):路由映射
ASP.NET的路由系統(tǒng):根據(jù)路由規(guī)則生成URL


表現(xiàn)為請求地址與目標(biāo)Controller和Action的動態(tài)映射的URL路由系統(tǒng)并不是專屬于ASP.NET MVC,而是直接建立在ASP.NET 中。ASP.NET通過URL路由系統(tǒng)實(shí)現(xiàn)了請求地址與物理文件的分離。[源代碼地址從這里下載]

浙公網(wǎng)安備 33010602011771號