【轉】深入分析 ASP.NET Mvc 1.0 – 1. 深入MvcHandler

MvcHandler是一個mvc程序真正開始的地方,因為你可以直接看到并調試它的源碼。

MvcHandler的主要代碼如下:

protected internal virtual void ProcessRequest(HttpContextBase httpContext) {AddVersionHeader(httpContext);// Get the controller typestring controllerName = RequestContext.RouteData.GetRequiredString("controller");// Instantiate the controller and call ExecuteIControllerFactory factory = ControllerBuilder.GetControllerFactory();IController controller = factory.CreateController(RequestContext, controllerName);if (controller == null) {throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.ControllerBuilder_FactoryReturnedNull,factory.GetType(),controllerName));}try {controller.Execute(RequestContext);}finally {factory.ReleaseController(controller);}}

這個方法的流程可以概括為: 找到Requst中的Controller name, 根據Controller name創建這個Controller, 執行這個Controller中執行被請求的

Action。

具體分析如下:

1. 添加Http Header
AddVersionHeader(httpContext);

添加一個Http Header: HTTP/1.1 200 OK?? …?? X-AspNetMvc-Version: 1.0…

2. 從路由表中找到請求的controller的名子
string controllerName = RequestContext.RouteData.GetRequiredString("controller");

獲取路由表中的controller name, 在下面的代碼中根據這個controller name在緩存中查找到對應的controller類型并生成controller類。

3. 返回一個IControllerFactory對象
IControllerFactory factory = ControllerBuilder.GetControllerFactory();

返回一個繼承自IControllerFactory接口的類的實例,這里默認返回DefaultControllerFactory類。 ControllerBuilder屬性是ControllerBuilder類的一個靜態實例,在mvc程序第一次啟動時才會執行ControllerBuilder類的默認構造函數, 在這個構造函數將DefaultControllerFactory類的一個實例傳入到SetControllerFactory()方法中, 這樣做的目地是定義GetControllerFactory()的具體返回類型。ControllerBuilder類的構造函數代碼如下:

public ControllerBuilder() {SetControllerFactory(new DefaultControllerFactory() {ControllerBuilder = this});}

所以想要改變GetControllerFactory()的默認返回類型的辦法就是在執行ControllerBuilder.GetControllerFactory()之前調用ControllerBuilder類中的

SetControllerFactory()方法,這個方法有兩個重載

public void SetControllerFactory(IControllerFactory controllerFactory) {if (controllerFactory == null) {throw new ArgumentNullException("controllerFactory");}_factoryThunk = () => controllerFactory;}public void SetControllerFactory(Type controllerFactoryType) {if (controllerFactoryType == null) {throw new ArgumentNullException("controllerFactoryType");}if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType)) {throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture,MvcResources.ControllerBuilder_MissingIControllerFactory,controllerFactoryType),"controllerFactoryType");}_factoryThunk = delegate() {try {return (IControllerFactory)Activator.CreateInstance(controllerFactoryType);}catch (Exception ex) {throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.ControllerBuilder_ErrorCreatingControllerFactory,controllerFactoryType),ex);}};}

只需要將我們自定義并繼承自IControllerFactory接口的類的一個實例或type傳入就可以。

4. 根據controller Name創建controller對象
IController controller = factory.CreateController(RequestContext, controllerName);

調用DefaultControllerFactory類中的CreateController方法創建controller類。 CreateController(…)方法的具體代碼如下:

public virtual IController CreateController(RequestContext requestContext, string controllerName){if (requestContext == null){throw new ArgumentNullException("requestContext");}if (String.IsNullOrEmpty(controllerName)){throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");}RequestContext = requestContext;Type controllerType = GetControllerType(controllerName);IController controller = GetControllerInstance(controllerType);return controller;}

這里比較簡單,首先執行GetControllerType(controllerName)找到對應的controll type, 再調用GetControllerInstance(controllerType) 反射出具體的controll類,先來看看GetControllerType(…)方法中的代碼:

protected internal virtual Type GetControllerType(string controllerName){if (String.IsNullOrEmpty(controllerName)){throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");}// first search in the current route's namespace collectionobject routeNamespacesObj;Type match;if (RequestContext != null && RequestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)){IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;if (routeNamespaces != null){HashSet<string> nsHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);match = GetControllerTypeWithinNamespaces(controllerName, nsHash);if (match != null){return match;}}}// then search in the application's default namespace collectionHashSet<string> nsDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);match = GetControllerTypeWithinNamespaces(controllerName, nsDefaults);if (match != null){return match;}// if all else fails, search every namespacereturn GetControllerTypeWithinNamespaces(controllerName, null /* namespaces */);}

RequestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)來返回一個namespace的集合, 一開始我對namespace很不理解,現在我明白了它的意思:在程序中不同的namespace下面可能會存在同名的controller,所以這里用namespace區分這些同名的但不同意義的controller。namespace可以在Global.asax.cs的RegisterRoutes(…)方法中指定,比如:

routes.MapRoute("Default",                                              // Route name"{controller}/{action}/{id}",                           // URL with parametersnew { controller = "Home", action = "Index", id = "" },  // Parameter defaultsnew { httpMethod = new HttpMethodConstraint("get", "post") },new string[]{"Namespace1"});

在繼續看GetControllerType(…)方法,在GetControllerType(string controllerName) 方法中最終都是通過調用GetControllerTypeWithinNamespaces(…)方法返回controller type的, 具體代碼如下:

private Type GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces){// Once the master list of controllers has been created we can quickly index into itControllerTypeCache.EnsureInitialized(BuildManager);IList<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);switch (matchingTypes.Count){case 0:// no matching typesreturn null;case 1:// single matching typereturn matchingTypes[0];default:// multiple matching types// we need to generate an exception containing all the controller typesStringBuilder sb = new StringBuilder();foreach (Type matchedType in matchingTypes){sb.AppendLine();sb.Append(matchedType.FullName);}throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.DefaultControllerFactory_ControllerNameAmbiguous,controllerName, sb));}}

首先將執行ControllerTypeCache.EnsureInitialized(BuildManager);? 他的作用是將程序中所有assembly中所有以Controller結尾的類放在緩存中,看一下

EnsureInitialized(…)方法的代碼:

ControllerBuilder.cs

public void EnsureInitialized(IBuildManager buildManager) { if (_cache == null) { lock (_lockObj) { if (_cache == null) { List<Type> controllerTypes = GetAllControllerTypes(buildManager); var groupedByName = controllerTypes.GroupBy( t => t.Name.Substring(0, t.Name.Length - "Controller".Length), StringComparer.OrdinalIgnoreCase); _cache = groupedByName.ToDictionary( g => g.Key, g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); } } } }

這是一個具有2級結構的緩存, 以controll name為key, 以Lookup<string, Type>對象為值保存到緩存中,而Lookup<string, Type>的結構是以namespace為key, 以

controller type為值的鍵值集合,這個2級結構的作用就是上面提到的用來解決不同namespace中同名controller的問題。

GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces) 方法中:

ControllerTypeCache.GetControllerTypes(controllerName, namespaces) 就是去找具有相同controllerName的controller type,不過這里有個問題就是如果沒有在

Global中或其它地方提供默認的namespace而不同namespace下存在同名的controller,就導致GetControllerTypes(…)方法返回的controller數量大于1,這時程序會在

switch語句處拋出一個異常,所里一定要注意,盡量不要在不同的namespace中定義同名的controller

當找到一個對應的 controller type后,就將這個type返回給上面的CreateController(RequestContext requestContext, string controllerName) 方法中調用

GetControllerType(controllerName); 方法的地方, 然后再調用GetControllerInstance(controllerType); 方法將方法反射成具體的controller類并返回到ProcessRequest(…)中,并依次執行controller.Execute(RequestContext) –> factory.ReleaseController(controller);?? 至此整個MvcHandler的流程執行完畢.

附上MvcHandler的時序圖:

MvcHandler_1

?

原文地址:深入分析 ASP.NET Mvc 1.0 – 1. 深入MvcHandler

轉載于:https://www.cnblogs.com/shong/archive/2010/08/17/1801576.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379052.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379052.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379052.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++11 右值引用與常量左值引用保存臨時變量(函數返回值)的底層分析

右值引用保存臨時變量&#xff08;函數返回值&#xff09;的問題 &#xff1a;臨時變量是右值 1、普通變量接收函數返回值&#xff1a; 2、右值引用變量接收函數返回值&#xff1a; 3、用const int& 和右值引用是一樣的效果&#xff0c;只是const int& 就不可以修改…

JavaScript中的位置協議屬性

JavaScript | 位置協議屬性 (JavaScript | Location protocol property) A protocol by definition simply implies a set or working rules that must be adhered to. A network protocol thus defines rules for communication between network devices. You must be familia…

axure源文件_Axure教程:實現網易云音樂有聲播放效果

為了方便講解&#xff0c;我們首先在桌面新建一個文件夾&#xff0c;命名為音樂。1、將自己想要演示播放的MP3音樂文件放在這個文件夾里面。2、給播放頁添加一個中繼器&#xff0c;隨便命名&#xff0c;我給它命名為【音樂地址鏈接器】&#xff0c;用來鏈接播放本地音樂文件。并…

2012年終總結

工作&#xff1a; 原來的文章也寫過&#xff0c;今年年初的時候&#xff0c;因為一些原因&#xff0c;成了另外一家公司的員工&#xff0c;但辦公地點還是在原來的公司。 這是一家外包公司&#xff0c;技術實力當然是比較強的&#xff0c;不過自己對于編程方面的興趣止不住的一…

PYTHON解析PE的模塊

RT&#xff0c;從GOOGLE CODE上找的。。雖然拿PYTHON解析PE的意義不大還有點麻煩…… 示例代碼&#xff1a; #coding:gbk import sys import pefile #from struct import * import structdef main():pe pefile.PE("ccalc.exe")pe.print_info()if __name__ "__…

ffplay分析(從啟動到讀取數據線程插入到字幕、音頻、視頻解碼前的隊列操作)

《ffplay的數據結構分析》 《ffplay分析&#xff08;視頻解碼線程的操作&#xff09;》 《ffplay分析&#xff08;音頻解碼線程的操作&#xff09;》 《ffplay 分析&#xff08;音頻從Frame(解碼后)隊列取數據到SDL輸出&#xff09;》 《ffplay分析 &#xff08;視頻從Frame(解…

python3和python2 優勢_python3和python2的區別

1.性能Py3.0運行 pystone benchmark的速度比Py2.5慢30%。Guido認為Py3.0有極大的優化空間&#xff0c;在字符串和整形操作上可以取得很好的優化結果。Py3.1性能比Py2.5慢15%&#xff0c;還有很大的提升空間。2.編碼Py3.X源碼文件默認使用utf-8編碼&#xff0c;這就使得以下代碼…

并發進程同步

P是荷蘭語Proberen&#xff08;測試&#xff09;的首字母。為阻塞原語&#xff0c;負責把當前進程由運行狀態轉換為阻塞狀態&#xff0c;直到另外一個進程喚醒它。也就是不好的一方面。 V是荷蘭語Verhogen&#xff08;增加&#xff09;的首字母。為喚醒原語&#xff0c;負責把一…

寄存器和pin_16x2 LCD的PIN圖和寄存器

寄存器和pinIn these years the LCD is finding widespread use. It has replaced the LEDs or other multi-segment LEDs.This is due to the following reasons: 近年來&#xff0c; LCD正在廣泛使用。 它已替換LED或其他多段LED&#xff0c;原因如下&#xff1a; The decli…

【Java IO知識】讀取中文亂碼問題解決

讀取文件主要是要設置讀取的輸入流如InputStreamReader時要設置讀取的解碼格式,要和文件本身的編碼格式相同,這樣讀取出來的才不會亂碼。 InputStreamReader reader new InputStreamInputReader(input, "編碼格式"); ANSI是"gbk" 注:必須是在讀取的時候就…

ffplay分析(視頻解碼線程的操作)

《ffplay的數據結構分析》 《ffplay分析&#xff08;從啟動到讀取線程的操作&#xff09;》 《ffplay分析&#xff08;音頻解碼線程的操作&#xff09;》 《ffplay 分析&#xff08;音頻從Frame(解碼后)隊列取數據到SDL輸出&#xff09;》 《ffplay分析 &#xff08;視頻從Fram…

【轉】.NET深入學習筆記(4):深拷貝與淺拷貝(Deep Copy and Shallow Copy)

今天繼續利用準備WSE安全開發文章的空閑時間&#xff0c;完善《.NET深入學習筆記》系列&#xff08;基本都是.Net重要的知識點&#xff0c;我都做了詳細的總結&#xff0c;是什么、為什么、和怎么實現&#xff09;。想必很多人也接觸過這兩個概念。做過C的人對深淺拷貝的概念一…

c語言數組-1_C數組-智能問題與解答

c語言數組-1C programming Arrays (One-D Array, Two-D Array) Aptitude Questions and Answers : In this section you will find C Aptitude Questions and Answers on One Dimensional (1D) and Two Dimensional (2D) array. C編程數組(一維數組&#xff0c;二維數組)能力問…

abap 添加alv上的工具欄的按鈕_Excel里的置頂功能——快速訪問工具欄

100萬職場人都在看后臺回復禮包領199元職場干貨大家好&#xff0c;我是小可~今天跟大家分享一個提高Excel操作效率的小技巧自定義你的快速訪問工具欄設置后的效果▼▼▼也就是把你最經常用到的兩三個功能放到快速訪問工具欄可以一眼就找到這些功能不需要靠快捷鍵或者功能選項卡…

用遞歸法求12+22+...+n2的值

思路分析: 談到遞歸,我個人會聯想到數學里面的通式。因為遞歸調用的函數的對應法則是相同的。例如這道題:f(x)=x。這個就是函數通式,只不過把每個求得的結果進行累加求和即可。用戶輸入5的時候,會出現f(5)=5,之后再進行x減一操作,執行f(4)=4,最后將每個進行累加即可。…

機器學習資料推薦

機器學習資料推薦 機器學習的資料 1:斯坦福大學視頻(作為入門教程&#xff0c;網易有中文字幕&#xff0c;而且講義也有翻譯版本&#xff09;20集200左右講義 2&#xff1a;機器學習 Tom M.Mitchell(雖然出版10多年&#xff0c;但是通俗易懂的內容&#xff0c;讓讀者對機器學習…

ffplay源碼(版本:ffmpeg-4.2.1)

ffplay源碼&#xff08;版本&#xff1a;ffmpeg-4.2.1&#xff09; /** Copyright (c) 2003 Fabrice Bellard** This file is part of FFmpeg.** FFmpeg is free software; you can redistribute it and/or* modify it under the terms of the GNU Lesser General Public* Lic…

stringwriter_Java StringWriter toString()方法與示例

stringwriterStringWriter類的toString()方法 (StringWriter Class toString() method) toString() method is available in java.io package. toString()方法在java.io包中可用。 toString() method is used to represent the buffer current value in terms of string. toStr…

編寫一個函數,計算下式當n=10和n=100的值。

思路分析: 首先,我個人看法:當我拿到這道題的時候,我會把它當成一道數學題對待。分子是動的,恒為一,分母是進行依次增加的。且奇數項為正,偶數項為負。因為設計運算出現的是分數,故,設計選取存儲類型為double。 找出問題: ①正負號問題、②分母問題、③累計求和問題…

POJ 1001 大數的乘法

對這道題的理解 大數的乘法 關鍵是 實型的 那么首先就是數出來小數點有幾位這個相信很簡單 從后面往前數剛開始0 不算接著就是遇到小數點結束如果沒有小數點 那么置為0 接著就是輸出地時候首先算出小數點的位置然后輸出 你想怎么樣都行 從后往前數這個時候輸出 那么就是你也…