日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

ASP.Net请求处理机制初步探索之旅 - Part 5 ASP.Net MVC请求处理流程

發(fā)布時間:2023/12/10 asp.net 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.Net请求处理机制初步探索之旅 - Part 5 ASP.Net MVC请求处理流程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

開篇:上一篇我們了解了在WebForm模式下一個Page頁面的生命周期,它經(jīng)歷了初始化Init、加載Load以及呈現(xiàn)Render三個重要階段,其中構造了頁面控件樹,并對頁面控件樹進行了大量的遞歸操作,最后將與模板結合生成的HTML返回給了瀏覽器。那么,在ASP.NET MVC模式下,一個頁面的生命周期又經(jīng)歷了哪些步湊呢?別急,本篇漫漫道來!

(1)Part 1:前奏

(2)Part 2:核心

(3)Part 3:管道

(4)Part 4:WebForm頁面生命周期

(5)Part 5:ASP.NET MVC請求處理流程

一、開放的ASP.NET MVC代碼

  2009年,Microsoft推出了ASP.NET MVC,也將ASP.NET MVC項目作為開源項目推送到了開源社區(qū)中,至今時間也過去快6年了,ASP.NET MVC已經(jīng)到了5.0的版本階段了。我們看到ASP.NET MVC從一個不完整的小孩長成一個日漸成熟的巨人,我們可以從開源社區(qū)找到ASP.NET MVC的源碼,相比之前我們需要Reflector進行反編譯查看,這次則輕松得多。

  這里我們選擇ASP.NET MVC 4的源碼作為分析對象,我已經(jīng)將其上傳到了網(wǎng)盤中,你可以通過下面這個地址進行下載:

  傳送門:http://pan.baidu.com/s/1bnF8ZPt

  下載完成后,打開ASP.NET MVC 4的源代碼,你會看到如下解決方案:這里我們主要關注System.Web.Mvc這個類庫項目

二、從MvcHandler.ProcessRequest開始

  從Part 3中我們知道了在請求處理管道中的第7個事件生成了MvcHandler,在第11和第12個事件之間調(diào)用了MvcHandler的ProcessRequest方法開始了ASP.NET MVC的處理響應之旅。那么,我們就從MvcHandler的ProcessRequest方法開始查看,一個ASP.NET MVC頁面是如何加載出來一個HTML頁的!

(1)Controller的激活

  ①借助HttpConetxtWrapper封裝HttpContext

protected virtual void ProcessRequest(HttpContext httpContext){HttpContextBase httpContextBase = new HttpContextWrapper(httpContext);ProcessRequest(httpContextBase);}

  可以看出,這里通過了一個基于包裝器(又稱裝飾者)模式實現(xiàn)的一個HttpContextWrapper類對HttpContext進行了一個封裝,并調(diào)用重載的另一個ProcessRequest方法進行繼續(xù)處理。

PS:有關ASP.NET MVC中HttpContext, HttpContextBase, HttpContextWrapper三者之間的聯(lián)系請參考:http://blog.csdn.net/sundacheng1989/article/details/10551091

  ②控制器工廠根據(jù)URL創(chuàng)建控制器

protected internal virtual void ProcessRequest(HttpContextBase httpContext){IController controller;IControllerFactory factory;ProcessRequestInit(httpContext, out controller, out factory);try{controller.Execute(RequestContext);}finally{factory.ReleaseController(controller);}}

  可以看出,這里通過調(diào)用ProcessRequestInit方法將上下文對象傳入進行處理,然后返回生成的控制器實例以及控制器工廠。因此,我們轉入ProcessRequestInit方法看看:

private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory){......string controllerName = RequestContext.RouteData.GetRequiredString("controller");factory = ControllerBuilder.GetControllerFactory();controller = factory.CreateController(RequestContext, controllerName);......}

  在這個方法中,首先根據(jù)RouteData路由數(shù)據(jù)取得要請求的Controller名稱,然后取得ControllerFactory(控制器工廠)對象,通過ControllerFactory來創(chuàng)建指定名稱的控制器,最后將控制器作為out參數(shù)傳遞出去。

  ③調(diào)用控制器的Execute方法進入Action

  具體實現(xiàn)了IController接口的Controller對象通過調(diào)用Excute方法開始執(zhí)行具體的Action,那么Action究竟又是怎樣被觸發(fā)的呢?

public interface IController{void Execute(RequestContext requestContext);}

(2)Action的觸發(fā)

  ①從ControllerBase的Excute方法開始 

public abstract class ControllerBase : IController{protected virtual void Execute(RequestContext requestContext){if (requestContext == null){throw new ArgumentNullException("requestContext");}if (requestContext.HttpContext == null){throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");}VerifyExecuteCalledOnce();Initialize(requestContext);using (ScopeStorage.CreateTransientScope()){ExecuteCore();}}// 抽象方法-讓Controller去具體實現(xiàn)protected abstract void ExecuteCore();}

  首先,Controller并沒有實現(xiàn)IController接口,而是Controller的基類ControllerBase實現(xiàn)了IController接口;然后,ControllerBase中定義了一個抽象方法ExcuteCore,讓其子類去具體執(zhí)行,這里主要是讓Controller類對象執(zhí)行這個方法。

  ②根據(jù)URL獲取Action名稱并準備觸發(fā)Action

public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer{protected override void ExecuteCore(){PossiblyLoadTempData();try{string actionName = GetActionName(RouteData);if (!ActionInvoker.InvokeAction(ControllerContext, actionName)){HandleUnknownAction(actionName);}}finally{PossiblySaveTempData();}}}

  首先,通過路由數(shù)據(jù)獲取Action名稱,例如請求URL為:http://xxx.com/Home/Index,這里獲取的Action名稱即為Index。然后,通過ActionInvoker.InvokeAction去執(zhí)行具體的Action。那么問題來了,這個ActionInvoker又是啥東東?我們先看看這個接口的定義:

public interface IActionInvoker{bool InvokeAction(ControllerContext controllerContext, string actionName);}

  通過查閱資料,我們發(fā)現(xiàn)原來是一個叫做ControllerActionInvoker的類實現(xiàn)了IActionInvoker接口,那么我們就去看看這個ControllerActionInvoker類吧。

  ③獲取Controller與Action的描述信息和過濾器信息

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName){......ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);if (actionDescriptor != null){FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);......} ......}

  看到這里,也許會有人問什么是描述信息?那么看到我們在開發(fā)中經(jīng)常給Controller或者Action添加的Attribute信息也許就不會感到陌生了:例如我們給某個名為Index的Action添加了[HttpPost]或者[HttpGet]特性,在請求時需要通過HTTP報文請求方式來區(qū)分這兩個Action。

  那么,什么又是過濾器信息?首先,過濾器涉及到一個叫做AOP(面向切面編程)的概念,我們可以通過前面的請求處理管道進行理解,雖然我們的ASP.NET頁面請求處理部分只是其中一小部分,但是在這部分執(zhí)行之前還經(jīng)歷了許多事件,在這之后又經(jīng)歷了許多事件,而這些事件都是可以自定義邏輯的,它們都可以叫做過濾器。ASP.NET MVC默認為我們提供了四種類型的過濾器(Filter),如下圖所示:

PS:對過濾器不熟悉的朋友可以看看我的另一篇對ASP.NET MVC基礎知識中的過濾器(Filter)的介紹:http://www.cnblogs.com/edisonchou/p/3932640.html

  ④獲取參數(shù)信息并開始真正執(zhí)行Action:Filter->Action->Filter

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName){......IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);......}

  通過上面所獲取的各種描述信息與過濾器信息找到Action并獲取所需的參數(shù),然后調(diào)用InvokeActionMethodWithFilters方法執(zhí)行Action。因此,再轉到InvokeActionMethodWithFilters方法看看:

protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters){ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);Func<ActionExecutedContext> continuation = () =>new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */){Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)};Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,(next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));return thunk();}

  在這個方法中,首先將上下文對象、描述信息、參數(shù)信息傳入InvokeActionMethod方法中,得到了一個Result對象。這個Result對象又是什么?轉到定義一看,原來不就是我們在開發(fā)中經(jīng)常返回的ActionResult類型嗎?

public ActionResult Result{get { return _result ?? EmptyResult.Instance; }set { _result = value; }}

  那么,在InvokeActionMethod方法中又是如何返回Result的呢?再次轉到定義看看:

protected virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters){object returnValue = actionDescriptor.Execute(controllerContext, parameters);ActionResult result = CreateActionResult(controllerContext, actionDescriptor, returnValue);return result;}

  在這個方法中,首先執(zhí)行了指定的Action,然后獲得了一個returnValue返回值,通過傳入返回值創(chuàng)建具體類型的ActionResult作為方法的返回值。這里需要注意的是,ActionResult是一個抽象類,像什么JsonResult、EmptyResult、ViewResult等都是其子類,而這里的CreateActionResult就是要創(chuàng)建其具體子類的實例并返回。

  現(xiàn)在將目光返回到InvokeActionMethodWithFilters方法中,看到代碼最后聲明了一個委托thunk,它是過濾器結合經(jīng)過反轉之后再合并之前聲明的委托continuation之后的一個新委托(它所持有的委托鏈順序會協(xié)調(diào)一致),目的是為了完成AOP的效果,比如首先要執(zhí)行Action執(zhí)行之前的過濾器,才能執(zhí)行Action方法。

  ⑤ActionResult閃亮登場:Filter->Result

public virtual bool InvokeAction(ControllerContext controllerContext, string actionName){......InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, challengeContext.Result ?? postActionContext.Result);......}

  現(xiàn)在回到InvokeAction這個主方法中,剛剛執(zhí)行完Action之后將結果都保存在了postActionContext中的Result中,現(xiàn)在繼續(xù)執(zhí)行過濾器(比如:可以對剛剛的Action結果進行一些處理),目的也是為了完成AOP的效果,比如執(zhí)行完Action之后,必須要執(zhí)行Action結束后的過濾器業(yè)務邏輯方法。那么,這里又是進行了什么操作呢?轉到InvokeActionResultWithFilters方法中去看看:

private ResultExecutedContext InvokeActionResultFilterRecursive(IList<IResultFilter> filters, int filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult){......if (filterIndex > filters.Count - 1){InvokeActionResult(controllerContext, actionResult);return new ResultExecutedContext(controllerContext, actionResult, canceled: false, exception: null);} IResultFilter filter = filters[filterIndex];filter.OnResultExecuting(preContext); ......int nextFilterIndex = filterIndex + 1;postContext = InvokeActionResultFilterRecursive(filters, nextFilterIndex, preContext, controllerContext, actionResult);......}

  首先,判斷過濾器執(zhí)行的序號是否已經(jīng)到了最后,如果不是,則繼續(xù)遞歸執(zhí)行本方法調(diào)用過濾器(這里對應的過濾器是OnResultExecuting事件,即在Result被生成時之前進行觸發(fā))。如果到了最后,則開始生成最終的ActionResult。看看這個InvokeActionResult方法,它是一個虛方法。

protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult){actionResult.ExecuteResult(controllerContext);}

(3)View的呈現(xiàn)

  我們知道ActionResult是一個抽象類,那么這個InvokeActionResult應該是由其之類來實現(xiàn)。于是,我們找到ViewResult,但是其并未直接繼承于ActionResult,再找到其父類ViewResultBase,它則繼承了ActionResult。于是,我們來查看它的ExecuteResult方法:

  ①約定大于配置的緣故

public override void ExecuteResult(ControllerContext context){......if (String.IsNullOrEmpty(ViewName)){ViewName = context.RouteData.GetRequiredString("action");}......}

  我們在日常開發(fā)中,總是被告知約定大于配置,View中的名字必須與Controller中Action的名字一致。在這了,我們知道了原因,可以看出,這里就是國通URL來取得ViewName然后去查找View的。

  ②找到ViewEngine視圖引擎并獲取ViewEngineResult

  首先,我們了解一下什么是ViewEngine視圖引擎:我們在ASP.NET MVC開發(fā)中一般會有兩個選擇,一個是aspx視圖引擎,另一個是ASP.NET MVC 3.0推出的Razor視圖引擎。Razor視圖引擎在減少代碼冗余、增強代碼可讀性和Visual Studio智能感知方面,都有著突出的優(yōu)勢。因此,Razor一經(jīng)推出就深受廣大ASP.Net開發(fā)者的喜愛。

public override void ExecuteResult(ControllerContext context){......ViewEngineResult result = null;if (View == null){result = FindView(context);View = result.View;}...... }

   這里通過FindView方法獲取到具體的View對象,而FindView又是ViewResultBase的一個抽象方法。這時,我們需要到ViewResult中去看看這個FindView方法。

protected override ViewEngineResult FindView(ControllerContext context){ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);if (result.View != null){return result;}......}

  這里通過在ViewEngineCollection視圖引擎集合中調(diào)用FindView方法返回一個ViewEngineResult對象,而View則作為屬性存在于這個ViewEngineResult對象之中。

  ③加載ViewData/TempData等數(shù)據(jù)生成ViewContext

protected override ViewEngineResult FindView(ControllerContext context){......TextWriter writer = context.HttpContext.Response.Output;ViewContext viewContext = new ViewContext(context, View, ViewData, TempData, writer);......}

  這里開始加載ViewData、TempData等數(shù)據(jù)生成ViewContext,可以在ViewContext的構造函數(shù)中看到如下代碼:

public ViewContext(ControllerContext controllerContext, IView view, ViewDataDictionary viewData, TempDataDictionary tempData, TextWriter writer): base(controllerContext){if (controllerContext == null){throw new ArgumentNullException("controllerContext");}if (view == null){throw new ArgumentNullException("view");}if (viewData == null){throw new ArgumentNullException("viewData");}if (tempData == null){throw new ArgumentNullException("tempData");}if (writer == null){throw new ArgumentNullException("writer");}View = view;ViewData = viewData;Writer = writer;TempData = tempData;}

  現(xiàn)在知道我們在Action方法中定義的那些ViewData或者TempData是在哪里被存入上下文了吧?

  ④開始Render:HTML頁面的呈現(xiàn)

protected override ViewEngineResult FindView(ControllerContext context){......View.Render(viewContext, writer);......}

  ViewContext上下文對象已生成好,TextWriter已經(jīng)拿到,現(xiàn)在就開始對View進行正式的呈現(xiàn)了,也就是返回給瀏覽器端請求的HTML。由于這里View對象是一個實現(xiàn)了IView接口的類對象,于是我們找到RazorView,但是它并未直接實現(xiàn)IView接口,于是我們找到它的父類BuildManagerCompiledView?

public abstract class BuildManagerCompiledView : IView{public virtual void Render(ViewContext viewContext, TextWriter writer){if (viewContext == null){throw new ArgumentNullException("viewContext");}object instance = null;Type type = BuildManager.GetCompiledType(ViewPath);if (type != null){instance = ViewPageActivator.Create(_controllerContext, type);}if (instance == null){throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,MvcResources.CshtmlView_ViewCouldNotBeCreated,ViewPath));}RenderView(viewContext, writer, instance);}}

  首先,通過ViewPath獲取View的類型(Type),這里也是通過BuildManger來完成的,每個cshtml都會被asp.net編譯成一個類。然后,通過反射生成了View的具體實例。最后,通過RendView方法進行下一步的呈現(xiàn)工作。RenderView是一個抽象方法,具體實現(xiàn)是在RazorView類或WebFormView類中。

protected override void RenderView(ViewContext viewContext, TextWriter writer, object instance){if (writer == null){throw new ArgumentNullException("writer");}WebViewPage webViewPage = instance as WebViewPage;if (webViewPage == null){throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,MvcResources.CshtmlView_WrongViewBase,ViewPath));}webViewPage.OverridenLayoutPath = LayoutPath;webViewPage.VirtualPath = ViewPath;webViewPage.ViewContext = viewContext;webViewPage.ViewData = viewContext.ViewData;webViewPage.InitHelpers();if (VirtualPathFactory != null){webViewPage.VirtualPathFactory = VirtualPathFactory;}if (DisplayModeProvider != null){webViewPage.DisplayModeProvider = DisplayModeProvider;}WebPageRenderingBase startPage = null;if (RunViewStartPages){startPage = StartPageLookup(webViewPage, RazorViewEngine.ViewStartFileName, ViewStartFileExtensions);}webViewPage.ExecutePageHierarchy(new WebPageContext(context: viewContext.HttpContext, page: null, model: null), writer, startPage);}

  在此方法中,首先將傳遞過來的實例轉換成了一個WebViewPage類的實例,然后將ViewContext、ViewData等數(shù)據(jù)賦給WebViewPage實例作為屬性,以便在View中獲取。然后,如果有開始頁則先執(zhí)行開始頁。最后,將HttpContext、Page與Model對象封裝為一個WebPageContext對象傳入ExecutePageHierarchy方法中進行執(zhí)行頁面的渲染。

  首先,我們從字面上來看,Hierarchy代表層次,那么方法名的意思大概是:根據(jù)層次執(zhí)行頁面。那么,什么是頁面的層次?

  在執(zhí)行ExecutePageHierachy這個方法來渲染View時,這個方法里面要完成相當多的工作,主要是ViewStart的執(zhí)行,和Layout的執(zhí)行。這里的困難之處在于對于有Layout的頁面來說,Layout的內(nèi)容是先輸出的,然后是RenderBody內(nèi)的內(nèi)容,最后還是Layout的內(nèi)容。如果僅僅是這樣的話,只要初始化一個TextWriter,按部就班的往里面寫東西就可以了,但是實際上,Layout并不能首先執(zhí)行,而應該是View的代碼先執(zhí)行,這樣的話View就有可能進行必要的初始化,供Layout使用。例如我們有如下的一個View:

@{ViewBag.Title = "Code in View";Layout = "_LayoutPage1.cshtml"; }

  這個Layout的內(nèi)容如下:

@{ Layout = "~/Views/Shared/_Layout.cshtml";ViewBag.ToView = "Data from Layout"; } <div>Data In View: @ViewBag.Title </div> <div>@RenderBody(); </div>

  這樣可以在頁面顯示Code in View字樣。 但是反過來,如果試圖在View中顯示在Layout里面的"Data from Layout"?則是行不通的,什么也不會被顯示。所以RenderBody是先于Layout中其他代碼執(zhí)行的,這種Layout的結構稱為?Page Hierachy

  在這樣的代碼執(zhí)行順序下,還要實現(xiàn)文本輸出的順序,因此asp.net mvc這里的實現(xiàn)中就使用了棧,這個棧是OutputStack,里面壓入了TextWriter。注意到這只是一個頁面的處理過程,一個頁面之中還會有Partial View 和 Action等,這些的處理方式都是一樣的,因此還需要一個棧來記錄處理到了哪個(子)頁面,因此還有一個棧,稱之為TemplateStack,里面壓入的是PageContext,PageContext維護了view的必要信息,比如Model之類的,當然也包括上面提到的OutputStack。有了上面的基本信息,下面看代碼,先看入口點:

public void ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) {PushContext(pageContext, writer);if (startPage != null) {if (startPage != this) {var startPageContext = Util.CreateNestedPageContext<object>(parentContext: pageContext, pageData: null, model: null, isLayoutPage: false);startPageContext.Page = startPage;startPage.PageContext = startPageContext;}startPage.ExecutePageHierarchy();}else {ExecutePageHierarchy();}PopContext();}

  這個方法中,第一步首先將pageContext入棧:PushContext

public void PushContext(WebPageContext pageContext, TextWriter writer){_currentWriter = writer;PageContext = pageContext;pageContext.Page = this;InitializePage();// Create a temporary writer_tempWriter = new StringWriter(CultureInfo.InvariantCulture);// Render the page into itOutputStack.Push(_tempWriter);SectionWritersStack.Push(new Dictionary<string, SectionWriter>(StringComparer.OrdinalIgnoreCase));// If the body is defined in the ViewData, remove it and store it on the instance// so that it won't affect rendering of partial pages when they call VerifyRenderedBodyOrSectionsif (PageContext.BodyAction != null){_body = PageContext.BodyAction;PageContext.BodyAction = null;}}

  第二步判斷是否存在ViewStart文件,如果有,就執(zhí)行startPage.ExecutePageHierachy()。如果不存在,則直接執(zhí)行ExecutePageHierachy()

public override void ExecutePageHierarchy(){......TemplateStack.Push(Context, this);try{// Execute the developer-written code of the WebPageExecute();}finally{TemplateStack.Pop(Context);} }

  這個方法就是將context壓棧,然后執(zhí)行相應的view的代碼,然后出棧。有了這些出入棧的操作,可以保證View的代碼,也就是Execute的時候的writer是正確的。Execute中的方法除去PartialView,Action之類的,最終調(diào)用的是WebPageBase中的WriteLiteral方法:

public override void WriteLiteral(object value){Output.Write(value);}

  這里的Output屬性是:

public TextWriter Output{get { return OutputStack.Peek(); }}

  在調(diào)用了Excute方法后,頁面上的HTML內(nèi)容基本輸出完畢,至此View就渲染完畢了

  第三步,pageContext出棧,主要是棧中的元素的清理工作。

三、一圖勝千言,總體上概覽

參考資料

致謝:本文參閱了大量園友的相關文章,向以下文章作者表示感謝!

(1)Darren Ji,《ASP.NET MVC請求處理管道聲明周期的19個關鍵環(huán)節(jié)》:http://www.cnblogs.com/darrenji/p/3795661.html

(2)初心不可忘,《綜述:ASP.NET MVC請求處理管道》:http://www.cnblogs.com/luguobin/archive/2013/03/15/2962458.html

(3)學而不思則罔,《ASP.NET Routing與MVC之二:請求如何激活Controller與Action》:http://www.cnblogs.com/acejason/p/3886968.html

(4)王承偉,《ASP.NET MVC請求原理與源碼分析》:http://bbs.itheima.com/thread-134340-1-1.html

(5)Ivony,《通過源代碼研究ASP.NET MVC中的Conroller和View》:http://www.cnblogs.com/Ivony/archive/2010/11/13/aspnet-mvc-by-source-1.html

(6)痞子一毛,《ASP.NET MVC請求處理圖解》:http://www.cnblogs.com/piziyimao/archive/2013/02/27/2935969.html

(7)蔣金楠,《ASP.NET MVC中的View是如何被呈現(xiàn)出來的》:http://www.cnblogs.com/artech/archive/2012/08/22/view-engine-01.html

(8)yinzixin,《深入ASP.NET MVC之七:ActionResult的執(zhí)行》:http://www.cnblogs.com/yinzixin/archive/2012/12/05/2799459.html?(一篇好文,值得閱讀)

總結

以上是生活随笔為你收集整理的ASP.Net请求处理机制初步探索之旅 - Part 5 ASP.Net MVC请求处理流程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。