我們知道一個請求最終通過一個具體的HttpHandler進行處理,而我們熟悉的用於表示一個Web頁面的Page對象就是一個HttpHandler,被用於處理基於某個.aspx文件的請求。我們可以通過HttpHandler的動態映射來實現請求地址與物理文件路徑之間的分離。實際上ASP.NET路由系統就是采用了這樣的實現原理。如下圖所示,ASP.NET路由系統通過一個注冊到當前應用的自定義HttpModule對所有的請求進行攔截,並通過對請求的分析為之動態匹配一個用於處理它的HttpHandler。HttpHandler對請求進行處理後將相應的結果寫入HTTP回復以實現對請求的相應。
上圖所示的作為請求攔截器的HttpModule類型為UrlRoutingModule。如下面的代碼片斷所示,UrlRoutingModule對請求的攔截是通過注冊表示當前應用的HttpApplication的PostResolveRequestCache事件實現的。
1: public class UrlRoutingModule : IHttpModule
2: {
3: //其他成員
4: public RouteCollection RouteCollection { get; set; }
5: public void Init(HttpApplication context)
6: {
7: context.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
8:
9: }
10: private void OnApplicationPostResolveRequestCache(object sender, EventArgs e);
11: }
UrlRoutingModule具有一個類型為RouteCollection的RouteCollection屬性,在默認的情況下引用這通過RouteTable的靜態屬性Routes表示的全局路由表。針對請求的HttpHandler的動態映射就實現在OnApplicationPostResolveRequestCache方法中,具體的實現邏輯非常簡單:通過HttpApplication獲得但前的HTTP上下文,並將其作為參數調用RouteCollection的GetRouteData方法得到一個RouteData對象。
通過RouteData的RouteHandler屬性可以得到一個實現了IRouteHandler的路由處理器對象,而調用後者的GetHttpHandler方法直接可以獲取對應的HttpHandler對象,而我們需要映射到當前請求的就是這麼一個 HttpHandler。下面的代碼片斷基本上體現了定義在UrlRoutingModule的OnApplicationPostResolveRequestCache方法中的動態HttpHandler映射邏輯。
1: public class UrlRoutingModule : IHttpModule
2: {
3: //其他成員
4: private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
5: {
6: HttpContext context = ((HttpApplication)sender).Context;
7: HttpContextBase contextWrapper = new HttpContextWrapper(context);
8: RouteData routeData = this.RouteCollection.GetRouteData(contextWrapper);
9: RequestContext requestContext = new RequestContext(contextWrapper, routeData);
10: IHttpHandler handler = routeData.RouteHandler.GetHttpHandler(requestContext);
11: context.RemapHandler(handler);
12: }
13: }
二、 PageRouteHandler V.S. MvcRouteHandler
通過前面的介紹我們知道對於調用RouteCollection的GetRouteData獲得的RouteData對象,其RouteHandler來源於匹配的Route對象。對於通過調用RouteCollection的MapPageRoute方法注冊的Route來說,它的RouteHandler是一個類型為PageRouteHandler對象。
由於調用MapPageRoute方法的目的在於實現請求地址與某個.aspx頁面文件之間的映射,所以我們最終還是要創建的Page對象還處理相應的請求,所以PageRouteHandler的GetHttpHandler方法最終返回的就是針對映射頁面文件路徑的Page對象。此外,MapPageRoute方法中還可以控制是否對物理文件地址實施授權,而授權在返回Page對象之前進行。
定義在PageRouteHandler中的HttpHandler獲取邏輯基本上體現在如下的代碼片斷中,兩個屬性VirtualPath和CheckPhysicalUrlAccess表示頁面文件的地址和是否需要對物理文件地址實施URL授權,它們在構造函數中被初始化,而最終來源於調用RouteCollection的MapPageRoute方法傳入的參數。
1: public class PageRouteHandler : IRouteHandler
2: {
3: public bool CheckPhysicalUrlAccess { get; private set; }
4: public string VirtualPath { get; private set; }
5: public PageRouteHandler(string virtualPath, bool checkPhysicalUrlAccess)
6: {
7: this.VirtualPath = virtualPath;
8: this.CheckPhysicalUrlAccess = checkPhysicalUrlAccess;
9: }
10: public IHttpHandler GetHttpHandler(RequestContext requestContext)
11: {
12: if (this.CheckPhysicalUrlAccess)
13: {
14: //Check Physical Url Access
15: }
16: return (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath(this.VirtualPath, typeof(Page))
17: }
18: }
ASP.NET MVC的Route對象是通過調用RouteCollection的擴展方法MapRoute方法進行注冊的,它對應的RouteHandler是一個類型為MvcRouteHandler的對象。如下面的代碼片斷所示,MvcRouteHandler用於獲取處理當前請求的HttpHandler是一個MvcHandler對象。MvcHandler實現對Controller的激活、Action方法的執行以及對請求的相應,毫不誇張地說,整個MVC框架實現在MvcHandler之中。
1: public class MvcRouteHandler : IRouteHandler
2: {
3: //其他成員
4: public IHttpHandler GetHttpHandler(RequestContext requestContext)
5: {
6: return new MvcHandler(requestContext)
7: }
8: }