1 引言 小弟在學習技術的時候有個毛病,往往喜歡先吃下一大桶理論然後再去實踐,不僅要知道這個 如何去做 還想知道 為什麼可以這麼做? ,但是事實是在工作時候項目經理可沒有興趣讓你去研究這些,特別是在小弟現在的公司是外包的情況下差點被辭退(這裡就不詳說了...) 顯然我也知道這樣不對,因為公司請你是來 ...
1 引言
小弟在學習技術的時候有個毛病,往往喜歡先吃下一大桶理論然後再去實踐,不僅要知道這個 如何去做 還想知道 為什麼可以這麼做? ,但是事實是在工作時候項目經理可沒有興趣讓你去研究這些,特別是在小弟現在的公司是外包的情況下差點被辭退(這裡就不詳說了...) 顯然我也知道這樣不對,因為公司請你是來快速完成任務的而不是讓你研究的!但是小弟依舊沒完全改掉這個毛病!或許說不知道工作和學習正常的分配吧!正因為這個毛病所以寫了這篇博客,還望和各位bigGod共同學習吧!!
2 進入正題
在進入正題前小弟希望閱讀者能瞭解最基本ASP.NET MVC 路由模板 ,小弟不會從伺服器(如:IIS)最低層的請求如何到達你的WebApplication進程進行說明,這個網上資料有很多如果想瞭解推薦http://www.cnblogs.com/lumnm/archive/2009/08/08/1541901.html這篇清晰易懂。這裡只講解ASP.NET MVC管道的http請求如何到達控制器操作。
- 創建實例
在說明之前首先我們創建一個預設的MVC項目(相信大家都會創建,這裡就不演示了),打開根目錄下\Global.asax.cs文件
在此文件下項目自動生成了一些代碼如下:
1 public class MvcApplication : System.Web.HttpApplication 2 { 3 protected void Application_Start() 4 { 5 AreaRegistration.RegisterAllAreas(); //配置區域路由 目的在大型複雜網站下方便管理路由 6 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); //配置過濾特性 7 RouteConfig.RegisterRoutes(RouteTable.Routes); //配置傳統路由 8 BundleConfig.RegisterBundles(BundleTable.Bundles); //配置捆綁縮減的JS和css文件 9 10 } 11 12 }
上面代碼中MVCApplication類的Application_Start()是整個程式的入口,在這裡可以配置整個web程式的全局屬性,而且我們的MvcApplication 繼承至HttpApplication,伺服器(如IIS)的請求都要經過HttpApplication的處理管道,管道內的處理過程是固定的,在伺服器處理請求的各個階段,依次觸發對應的事件,便於程式員在不同的階段完成自定義的處理工作。查看HttpApplication的元數據(滑鼠右鍵轉到定義)發現有24個事件,但是我們一般關心是如下19個管道事件:
(註:圖片轉至 木宛城主的博客:http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-apply-asp-net-identity-authentication.html)
在MVC里處理請求的是UrlRoutingModule類,它是一個實現了IHttpModule介面的類,那這個介面的作用是什麼呢?只要繼承至IHttpModule介面的類就可以訂閱HttpApplication生命周期的各個事件。下麵是IHttpModule介面的代碼:(非常簡單)
1 public interface IHttpModule 2 { 3 void Dispose(); 4 void Init(HttpApplication context); 5 }
Init方法是實現HttpModule功能的主要方法,它有一個HttpApplication類型的context參數,這個參數允許訪問當前HttpApplication的環境以此來訂閱註冊處理請求過程中不同的事件,為了更好的理解UrlRoutingModule類的運行機制,我寫個小例子,該例修改http輸出流,在每個輸出流加上請求時間和請求處理完畢時間。在你自己隨意位置(如:/Models或重新創建一個類庫項目)下添加一個類,然後讓它繼承IHttpModule介面並實現。代碼如下:
1 public class MyModule : IHttpModule 2 { 3 4 private HttpApplication _application = null; 5 6 public void Init(HttpApplication context) 7 { 8 9 _application = context; 10 11 string reqTime = string.Empty; //請求時間 12 string resTime = string.Empty; //請求完畢時間 13 14 // //通過訂閱HttpApplication的BeginRequest事件,這個事件在收到一個http請求時發生 15 _application.BeginRequest += (obj, e) => 16 { 17 reqTime = string.Format("請求時間:{0}", DateTime.Now.ToString()); 18 }; 19 20 //通過訂閱HttpApplication的EndRequest事件,這個事件會在把響應內容發送給客戶端前觸發 21 _application.EndRequest += (obj, e) => 22 { 23 resTime = string.Format("請求處理後時間:{0}", DateTime.Now.ToString()); 24 25 _appliction.Context.Response.Write(reqTime+"<br/>"+resTime); //在每個響應內容流里寫入當前請求的時間 26 27 }; 28 } 29 }
為了使用這個模塊,我們要在請求處理管道上包含該模塊。為此我們配置web.config文件使其包含它一個引用。在web.config文件的<httpModules>節點下添加:
<add name="ClassName" type="NameSpace.ClassName,AssemblyName" />
我的例子ClassName是MyModule,命名空間是MvcDemo.Models,所以我的節點如下
<httpModules>
<add name="MyModule" type="MvcDemo.Models.MyMoule,MvcDemo" />
</httpModules>
然後按F5運行你就會發現每個頁面多了2排分別表示請求和請求完畢的時間的文字
現在你應該大概瞭解UrlRoutingModule類的功能了,路由機制就是利用這個原理,它通過註冊HttpApplication事件參與到管道處理請求中,具體是訂閱HttpApplication某個階段的事件。UrlRoutingModule訂閱了PostResolveRequestCache 事件,實現url的映射。為什麼是該事件呢?因為該事件的下一步就要完成請求和物理文件的映射,所以必須要此之前進行攔截。
細心的朋友可能發現在 <httpModules>節點下並沒有添加 UrlRoutingModule類的引用啊,其實web.config配置可以分為本站和全局,全局的配置在C:\Windows\Microsoft.NET\Framework\v版本號\Config\web.config文件里
在瞭解了UrlRoutingModule類的功能下,我們繼續查看它如何映射到控制器操作的,核心源代碼如下:
public class UrlRoutingModule : IHttpModule { protected virtual void Init(HttpApplication application) { if (application.Context.Items[_contextKey] == null) { application.Context.Items[_contextKey] = _contextKey;
//註冊訂閱HttpApplication對象的PostResolveRequestCache事件 application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);//事件觸發時會執行下麵的方法 } } private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) { HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context); this.PostResolveRequestCache(context); } }
從源碼看出UrlRoutingModule其實跟我們上面那例子差不多都是通過註冊事件來執行相應的代碼功能的。
在瞭解更多源碼之前,我們先來瞭解下路由的基本知識,路由其實並非是ASP.NET MVC的一個特性,其實它僅在MVC1.0的前期階段是MVC獨有的,但是後面ASP.NET團隊把它變成了獨立的項目,所以它並不依賴於MVC,這是個題外話。我們現在要做的是依然打開根目錄下\Global.asax.cs文件
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() {
//這裡就是我們配置路由的代碼方法 RouteConfig.RegisterRoutes(RouteTable.Routes); //配置傳統路由 } }
//方法的代碼在/App_Start文件目錄下的RouteConfig.cs文件
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.MapMvcAttributeRoutes(); //開啟特性理由 routes.IgnoreRoute("{resource}.axd/{*pathInfo}");//忽略映射文件 routes.MapRoute( name: "Default1", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); routes.MapRoute( name: "Default2", url: "Side/{Year}/{Month}/{Day}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
這裡的路由知識我不說明,因為我相信閱讀者都能看懂上面的代碼,它就是添加了2個傳統路由的路由模板,我們關註的對象是傳進給RegisterRoutes方法的RouteTable.Routes參數,RouteTable其實就是一個封裝成全局對象的路由集合源代碼如下(非常簡單)
public class RouteTable { private static RouteCollection _instance = new RouteCollection(); public static RouteCollection Routes => _instance; }
可以看見RouteTable.Routes參數其實就是一個全局的RouteCollection對象,這樣你的路由集合保證了全局唯一性,調用也方便。通過RouteCollection集合對象我們可以通過它的Add添加Reute對象。
1 routes.MapRoute( 2 name: "Default1", 3 url: "{controller}/{action}/{id}", 4 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 5 ); 6 7 8 public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) 9 { 10 11 Route route = new Route(url, new MvcRouteHandler()) { 12 Defaults = CreateRouteValueDictionaryUncached(defaults), 13 Constraints = CreateRouteValueDictionaryUncached(constraints), 14 DataTokens = new RouteValueDictionary() 15 };
16 ConstraintValidation.Validate(route); 17 if ((namespaces != null) && (namespaces.Length > 0)) 18 {
19 route.DataTokens["Namespaces"] = namespaces; 20 } 21 routes.Add(name, route); 22 return route; 23 }
可以從源代碼看出實踐RouteCollection集合對象的MapRoute方法也是調用的Add方法添加的路由模板。
---------------------------------------------------------------------------------未完待續--------------------------------------------------------------------------------------------------------------