對于Asp.Net Web Forms應用來說,請求的Url都是對應一個具體的物理文件(http://xxx.com/default.aspx)。這樣的Url與具體物理文件緊密綁定在一起,帶來了諸多方便的局限:可讀性、SEO優化等。為了解決這些局限性,微軟引入了URL路由系統。下面通過一個Demo來剖析一下Asp.Net的路由系統。
創建一個空的WebForm應用程序,在Global.asax.cs文件中加入如下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class Global : System.Web.HttpApplication { protected void Application_Start( object sender, EventArgs e) { //處理匹配的文件 RouteTable.Routes.RouteExistingFiles = true ; //url默認值 RouteValueDictionary defaults = new RouteValueDictionary() { { "name" , "wuwenmao" }, { "id" , "001" } }; //路由約束 RouteValueDictionary constraints = new RouteValueDictionary() { { "name" , @"\w{2,10}" }, { "id" , @"\d{3}" } }; //與路由相關的值,但不參與路由是否匹配URL模式 RouteValueDictionary dataTokens = new RouteValueDictionary() { { "defaultName" , "wuwenmao" }, { "defaultId" , "001" } }; RouteTable.Routes.MapPageRoute( "default" , "employees/{name}/{id}" , "~/Default.aspx" , false , defaults, constraints, dataTokens); } } |
新建名為Default的WebForm頁面,頁面代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication2.Default" %> <!DOCTYPE html> < html xmlns = "http://www.w3.org/1999/xhtml" > < head runat = "server" > < meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" /> < title ></ title > </ head > < body > < form id = "form1" runat = "server" > < h1 >這是Default.aspx頁面</ h1 > < div > RouteData中Values: < ul > <% foreach (var value in RouteData.Values) { %> < li > <%=value.Key %>=<%=value.Value %> </ li > <%} %> </ ul > RouteData中DataTokens: < ul > <% foreach (var value in RouteData.DataTokens) { %> < li > <%=value.Key %>=<%=value.Value %> </ li > <%} %> </ ul > </ div > </ form > </ body > </ html > |
輸入路徑為一下三種,得到的結果都是一樣的:
http://localhost:2947/employees/wuwenmao/001
http://localhost:2947/employees/wuwenmao
http://localhost:2947/employees/
原因是因為注冊路由的時候,為路由模板中的變量設置了默認值,所以當用以上三種url時是等效的。
回頭看Global文件中,在注冊路由時還設置了一個變量:
這是使用正則規則限定了路由模板中變量的值,請求url中對應的變量值只有與正則匹配才能正確請求,否則返回404錯誤。如id值長度大于3時:
上面通過一個簡單的例子體驗了一下Asp.Net路由系統,下面我們通過翻看源碼來剖析一下Asp.Net路由系統的實現原理。
首先,我們Global文件中使用以下語句注冊一個路由時,實際上是向全局路由表添加一個路由。
通過Reflector工具,我們可以看到:
現在有個問題,在注冊好路由之后,Asp.Net是如何使用路由系統的呢?實際上,Asp.Net路由系統是通過注冊一個HttpModule對象,由這個HttpModule對象實現針對請求進行攔截,然后動態映射到用于處理當前請求的HttpHandler對象中,最后通過HttpHandler對象對請求進行處理并響應。這個HttpModule實際上就是UrlRoutingModule,我們在啟動Asp.Net程序時,通過Global文件中的Modules屬性可以驗證,從下面截圖可以看到,Modules屬性中包含了已經注冊的HttpModule,其中就包含UrlRoutingModule:
在這個UrlRoutingModule里面,又進行了哪些跟路由相關的操作呢,我們還是繼續翻看源碼:
通過上面的源碼查看,我們可以看出,當有請求來到時,Asp.Net通過注冊的UrlRoutingModule模塊攔截了請求,然后從全局路由表中查找匹配的RouteData,如果找得到,根據HttpApplication獲取到對應的HttpHandler,然后將其映射到當前請求上下文中,供后續的管道事件用以處理當前請求。
下面我們繼續翻看源碼,剖析一下UrlRoutingModule是怎么從全局路由表中獲取RouteData的:
從上面可以看到,UrlRoutingModule中調用全局路由表的GetRouteData,實際上是依次調用注冊的每個Route的GetRouteData,返回第一個匹配的RouteData,如果注冊的路由都不匹配,返回null。
下面我們再來看看Route里面的GetRouteData做了些什么:
Match方法:
通過依次調用Route的GetRouteData方法,在GetRouteData方法中做了如下操作:
1、調用了ParsedRoute類型的Match方法進行請求Url和注冊在當前Route對象中的路由模板的匹配工作,如果沒有匹配,直接返回null;
2、如果請求Url和當前Route對象的路由模板匹配了,常見RouteData對象;
3、根據注冊路由信息時定義的約束條件來檢驗當前請求Url是否通過,不通過返回null;
4、為RouteData對象的Values和DataTokens賦值操作;
5、返回RouteData對象;
到此,Asp.Net的路由系統基本上剖析完畢,還有很多細節限于篇幅沒辦法一一剖析。
總結:
通過以上的剖析,我們整理一下思路,對Asp.Net路由系統所做的工作做個總結:首先,我們在Global中注冊了Route對象,然后通過在Asp.Net注冊的HttpModule模塊UrlRoutingModule進行攔截請求Url,之后從全局路由表RouteTables.Routes中依次調用Route對象的GetRouteData進行請求Url和注冊路由信息的匹配,返回第一個匹配的RouteData,查找完整個RouteTables.Routes后沒有匹配到,返回null,最終會返回404給前端頁面。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/wuwenmao/p/6398320.html