为了搞清楚ASP.NET MVC的请求过程,我们计划从结果追踪到源头。使用VS2012创建一个空白的ASP.NET MVC项目

然后创建一个HelloController

创建一个HelloView。在Views文件夹下创建一个Hello的文件夹,然后创建一个名为Index的View

然后再view中输入hello asp.net mvc4

点击App_Start下的RouteConfig.cs,更改Default的路由的Controller为Hello

OK,点击调适按钮,你可以得到如下结果:

OK.我们就从这里出发,开始分析用户的请求是如何被处理的。首先我们再来看HelloController的代码

恩,我们就从这里开始分析吧。

(1)我们可以看到Index()返回的是一个View()。这个View()来自基类

protectedinternalViewResult View()

{

return View(viewName: null, masterName: null, model: null);

}

(2) 我们继续找到对应的方法:

protectedinternalvirtualViewResult View(string viewName, string masterName, object model)

{

if (model != null)

{

ViewData.Model = model;

}

returnnewViewResult

{

ViewName = viewName,

MasterName = masterName,

ViewData = ViewData,

TempData = TempData,

ViewEngineCollection = ViewEngineCollection

};

}

通过C#4.0的方式,创建了一个ViewResult对象。通过查看ViewResult类,我们可以发现其继承了ViewResultBase类,我们可以用UML来表示出上面5个属性在两个类之间的关系

(3) 再看第一步,传入的三个参数都为null,那么在创建ViewResult对象时,其他的几个属性ViewData,TempData和ViewEngineCollection的值是从哪里来的呢?

通过上面的图,我们知道,这三个属性均继承自ViewResultBase类,因此我们去分析ViewResultBase类。

ViewData

get

{

if (_viewData == null)

{

_viewData = newViewDataDictionary();

}

return _viewData;

}

set { _viewData = value; }

TempData

get

{

if (_tempData == null)

{

_tempData = newTempDataDictionary();

}

return _tempData;

}

set { _tempData = value; }

ViewEngineCollection

get { return _viewEngineCollection ?? ViewEngines.Engines; }

set { _viewEngineCollection = value; }

到目前为止,我们并没有看到任何代码调用这些属性的Set方法,那么我们推断这三个属性的返回值分别是

ViewData

newViewDataDictionary();

TempData

newTempDataDictionary();

ViewEngineCollection

ViewEngines.Engines;

为了验证我们的推断,我们可以通过调试来验证。

恩,这下明确了。没有问题。那么我们现在需要知道ViewEngineCollection这个属性是如何赋值的。

(4) 我们查看ViewEngines这个类的代码

publicstaticclassViewEngines

{

privatestaticreadonlyViewEngineCollection _engines = newViewEngineCollection

{

newWebFormViewEngine(),

newRazorViewEngine(),

};

publicstaticViewEngineCollection Engines

{

get { return _engines; }

}

}

原来如此,原来如此,ViewEngine是一个静态类。仅仅包含了一个静态的构造函数一个静态的属性。返回一个ViewEngineCollection。那么ASP.NET MVS怎么就知道该使用哪个ViewEngine呢?

在RouteConfig.cs文件中,我们注册了默认的Route

routes.MapRoute(

name: "Default",

url: "{controller}/{action}/{id}",

defaults: new { controller = "Hello", action = "Index", id = UrlParameter.Optional }

);

因此,系统可以知道当前的Controller为HelloController,Action为Index。ASP.NET MVC Framework使用ControllerFactory创建Controller实例,然后通过ControllerActionInvoker通过反射的方式把Action转化为HelloController类Index方法的调用,最后调用ViewResultBase的ExecuteResult方法,把方法返回的结果传递给对应的View,并在该View中把最终结果呈现给用户。

这个过程相当复杂,设计众多的类。我们先来看一下概览图,我只列举了重要的类,

这几个类的生命周期应该是这样子的:

OK,下面我们来详细叙述一下整个过程,并加以代码分析

1)ControllerActionInvoker的InvokeAction被调用。那么它被谁调用呢,通过类图,我们知道,InvokeAction方法是实现了IActionVoker接口,我们通过调用关系图,可以知道该方法是Controller.cs的ExecuteCore方法调用

protectedoverridevoid ExecuteCore()

{

// If code in this method needs to be updated, please also check the BeginExecuteCore() and

// EndExecuteCore() methods of AsyncController to see if that code also must be updated.

PossiblyLoadTempData();

try

{

string actionName = RouteData.GetRequiredString("action");

if (!ActionInvoker.InvokeAction(ControllerContext, actionName))

{

HandleUnknownAction(actionName);

}

}

finally

{

PossiblySaveTempData();

}

}

而Controller类继承了ControllerBase类,ControllerBase类的Execute方法调用了Controll的ExecuteCore方法

protectedvirtualvoid Execute(RequestContext requestContext)

{

if (requestContext == null)

{

thrownewArgumentNullException("requestContext");

}

if (requestContext.HttpContext == null)

{

thrownewArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");

}

VerifyExecuteCalledOnce();

Initialize(requestContext);

using (ScopeStorage.CreateTransientScope())

{

ExecuteCore();

}

}

并且,ControllerBase的Execute方法实现了IController接口,此方法被MvcHandler类处理请求时调用

protectedinternalvirtualvoid ProcessRequest(HttpContextBase httpContext)

{

SecurityUtil.ProcessInApplicationTrust(() =>

{

IController controller;

IControllerFactory factory;

ProcessRequestInit(httpContext, out controller, out factory);

try

{

controller.Execute(RequestContext);

}

finally

{

factory.ReleaseController(controller);

}

});

}

OK,我们就到这里吧,关于用户的请求如何到MvcHandler这里,我将另外写一篇文章来介绍。当然,园子里很多同学也已经写了很多,大家可以去查阅,比如:…..

2)下面,我们来看一下ControllerActionInvkoer类的InvokeAction方法具体做了哪些事情

publicvirtualbool InvokeAction(ControllerContext controllerContext, string actionName)

{

if (controllerContext == null)

{

thrownewArgumentNullException("controllerContext");

}

if (String.IsNullOrEmpty(actionName))

{

thrownewArgumentException(MvcResources.Common_NullOrEmpty, "actionName");

}

ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);

ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);

if (actionDescriptor != null)

{

FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);

try

{

AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);

if (authContext.Result != null)

{

// the auth filter signaled that we should let it short-circuit the request

InvokeActionResult(controllerContext, authContext.Result);

}

else

{

if (controllerContext.Controller.ValidateRequest)

{

ValidateRequest(controllerContext);

}

IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);

ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);

InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);

}

}

catch (ThreadAbortException)

{

// This type of exception occurs as a result of Response.Redirect(), but we special-case so that

// the filters don't see this as an error.

throw;

}

catch (Exception ex)

{

// something blew up, so execute the exception filters

ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);

if (!exceptionContext.ExceptionHandled)

{

throw;

}

InvokeActionResult(controllerContext, exceptionContext.Result);

}

returntrue;

}

// notify controller that no method matched

returnfalse;

}

最重要的代码,我用黄色highlights出来了。下面我们来分析一下这三行代码

2)A 检查Action是否有Authroization特性,如果有进行验证。验证的具体代码如下

if (AuthorizeCore(filterContext.HttpContext))

{

HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;

cachePolicy.SetProxyMaxAge(newTimeSpan(0));

cachePolicy.AddValidationCallback(CacheValidateHandler, null/* data */);

}

else

{

HandleUnauthorizedRequest(filterContext);

}

如果验证成功,那么AuthorizationContext的Result属性为NULL,否则返回newHttpUnauthorizedResult();HttpUnauthorizedResult继承HttpStatusCodeResult,而HttpStatusCodeResult继承ActionResult。如果验证失败,那么调用InvokeActionResult方法。

protectedvirtualvoid InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)

{

actionResult.ExecuteResult(controllerContext);

}

可见,InvokeActionResult方法,实际调用ActionResult的ExecuteResult方法。在当前的情况下(验证失败),此时的authContext.Result的具体类型是HttpUnauthorizedResult,它继承了HttpStatusCodeResult, 所以InvokeActionResult进入的是HttpStatusCodeResult类的ExecuteResult方法

publicoverridevoid ExecuteResult(ControllerContext context)

{

if (context == null)

{

thrownewArgumentNullException("context");

}

context.HttpContext.Response.StatusCode = StatusCode;

if (StatusDescription != null)

{

context.HttpContext.Response.StatusDescription = StatusDescription;

}

}

设置完StatusCode和StatusDescription之后,将直接返回,不会寻找对应的View。

2)B 如果不涉及验证,或者验证成功。那么首先获取action的参数IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
比如我们当前的请求为/Hello/Index/2013
那么parameters的值为[id, 2013].请注意这个和你注册RouteData的格式相关联。如果你RouteData注册为为{controlller}/{action}/{no},那么parameters的值为[no,2013]

接着,获取postActionContext对象ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);该方法包括两个主要的步骤:其一为创建ActionExecutedContext,其二为调用与Action对应的Controller的方法,并将方法的结果保存在ActionResult之上。这个ActionResult的具体类型可能是ContentResult,ViewResult,或者JsonResult等等继承了ActionResult的各个子类。

为了模拟这个过程,我们创建如下两行代码模拟上面过程的执行结果:

// 返回ContentResult

ActionResult result1 = CreateActionResult(ControllerContext, actionDescriptor, "123");

// 返回ViewResult

ActionResult result2 = CreateActionResult(ControllerContext, actionDescriptor, View());

OK,如果是ContentResult,那么它的ExecuteResult方法如下

publicoverridevoid ExecuteResult(ControllerContext context)

{

if (context == null)

{

thrownewArgumentNullException("context");

}

HttpResponseBase response = context.HttpContext.Response;

if (!String.IsNullOrEmpty(ContentType))

{

response.ContentType = ContentType;

}

if (ContentEncoding != null)

{

response.ContentEncoding = ContentEncoding;

}

if (Content != null)

{

response.Write(Content);

}

}

可见,直接把内容输出到浏览器

如果是ViewResult,那么首先调用基类ViewResultBase的ExecuteResult方法

publicoverridevoid ExecuteResult(ControllerContext context)

{

if (context == null)

{

thrownewArgumentNullException("context");

}

if (String.IsNullOrEmpty(ViewName))

{

ViewName = context.RouteData.GetRequiredString("action");

}

ViewEngineResult result = null;

if (View == null)

{

result = FindView(context);

View = result.View;

}

TextWriter writer = context.HttpContext.Response.Output;

ViewContext viewContext = newViewContext(context, View, ViewData, TempData, writer);

View.Render(viewContext, writer);

if (result != null)

{

result.ViewEngine.ReleaseView(context, View);

}

}

该方法调用ViewResult的FindView方法

protectedoverrideViewEngineResult FindView(ControllerContext context)

{

ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName);

if (result.View != null)

{

return result;

}

// we need to generate an exception containing all the locations we searched

StringBuilder locationsText = newStringBuilder();

foreach (string location in result.SearchedLocations)

{

locationsText.AppendLine();

locationsText.Append(location);

}

thrownewInvalidOperationException(String.Format(CultureInfo.CurrentCulture,

MvcResources.Common_ViewNotFound, ViewName, locationsText));

}

自此,Controller的执行结果与View建立关联,最后ASP.NET MVC Framework把结果通过对应的视图显示到用户的浏览器中

FindView包含三个参数:context这个是ControllerContext;第二个是ViewName,它的值为ViewName = context.RouteData.GetRequiredString("action"),其实就是Action的值;第三个是MasterName,我们的例子中为空。

那么如何找到WebFormView呢?

我们在HomeController中创建一个List方法,并在View/Hello文件夹下创建List.aspx文件

publicActionResult List()

{

return View();

}

然后执行调适:

由于当前默认的请求是/Hello/Index,因此MVC Framework会自动寻找
~/Views/Hello/Index.cshtml

~/Views/Hello/Index.vbhtml

~/Views/Shared/Index.cshtml

~/Views/Shared/Index.cshtml

实际上,Razor引擎不会真正的在硬盘上寻找上面的文件,因为,这些文件都已经编译成C#的类。所以Razor在编译后的类中寻找对应的视图。

Next

  1. How the controller is initialized?
  2. How the request comes to MvcHandler
  3. View, IView, ViewEngine, RazorViewEngile, RazorView

大多人应该都知道用户向IIS发送一个ASP.NET请求后,IIS处理请求并向用户返回对应的结果。也有人知道,当一个ASP.NET请求到达IIS后,进入CLR,然后由HttpApplication创建HttpContext并找到对应的HttpHandler处理请求。最后把结果返回到用户端。那么从进入CLR之后,一个ASP.NET请求的生命周期具体是怎么样的,要经历那些重要的对象呢? 本文主要介绍这两个方面。

ASP.NET Request Liftcycle

首先,我们来看看一个request在进入CLR之前,发生了什么?

在IIS6和IIS7中,所有的HTTP请求均由HTTP侦听器捕获。那么HTTP侦听器是什么,其实就是http.sys,它运行在内核级别。至于具体什么是内核模块,以及如何运行,超出本文的范畴。如果你有兴趣,请自己查阅MSDN.

HTTP侦听器把捕获到的HTTP请求放到对应的应用程序池的请求队列中。所谓应用程序池

HttpRuntime, HttpApplicationFactory, HttpApplication,HttpContext, HttpHandler, HttpModule

动态编译

[ASP.NET MVC]视图是如何呈现的的更多相关文章

  1. [ASP.NET MVC]视图是如何呈现的 (续)

    在上一篇文章中,我们知道了通过Controller执行ActionResult的Execute可以找到对应Controler对应的ViewEngine,然后在View中把Action的结果显示出来.那 ...

  2. ASP.NET MVC 视图(五)

    ASP.NET MVC 视图(五) 前言 上篇讲解了视图中的分段概念.和分部视图的使用,本篇将会对Razor的基础语法简洁的说明一下,前面的很多篇幅中都有涉及到视图的调用,其中用了很多视图辅助器,也就 ...

  3. ASP.NET MVC 视图(四)

    ASP.NET MVC 视图(四) 前言 上篇对于利用IoC框架对视图的实现进行依赖注入,最后还简单的介绍一下自定义的视图辅助器是怎么定义和使用的,对于Razor语法的细节和辅助器的使用下篇会说讲到, ...

  4. ASP.NET MVC 视图(一)

    ASP.NET MVC 视图(一) 前言 从本篇开始就进入到了MVC中的视图部分,在前面的一些篇幅中或多或少的对视图和视图中的一些对象的运用进行了描述,不过毕竟不是视图篇幅说的不全面,本篇首先为大家讲 ...

  5. Asp.net MVC 视图引擎

    Asp.net MVC视图引擎有两种: 1.ASPX View Engine 这个做过WebForm的人都清楚 设计目标:一个用于呈现Web Form页面的输出的视图引擎. 2.Razor View ...

  6. ASP.NET MVC 之Model的呈现

    ASP.NET MVC 之Model的呈现(仅此一文系列三) 本文目的 我们来看一个小例子,在一个ASP.NET MVC项目中创建一个控制器Home,只有一个Index: public class H ...

  7. 【ASP.NET MVC系列】浅谈ASP.NET MVC 视图

    ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...

  8. ASP.NET MVC 视图(三)

    ASP.NET MVC 视图(三) 前言 上篇对于Razor视图引擎和视图的类型做了大概的讲解,想必大家对视图的本身也有所了解,本篇将利用IoC框架对视图的实现进行依赖注入,在此过程过会让大家更了解的 ...

  9. ASP.NET MVC 视图(二)

    ASP.NET MVC 视图(二) 前言 上篇中对于视图引擎只是做了简单的演示,对于真正的理解视图引擎的工作过程可能还有点模糊,本篇将会对由MVC框架提供给我们的Razor视图引擎的整个执行过程做一个 ...

随机推荐

  1. 给力开源,.Net开源地址大收集

    一.基础类库: 1,项目名:Npoi 资源星级:★★★ (个人评的) 介绍:NPOI - 一个能帮助你直接读写office文件流的库 系统教程:http://www.cnblogs.com/tonyq ...

  2. 添加script标签、添加事件

    添加script标签 var _hmt = _hmt || [];    (function () {        var hm = document.createElement("scr ...

  3. [Python设计模式] 第8章 学习雷锋好榜样——工厂方法模式

    github地址:https://github.com/cheesezh/python_design_patterns 简单工厂模式 v.s. 工厂方法模式 以简单计算器为例,对比一下简单工厂模式和工 ...

  4. PAM 認 證 模 組

    作者:陳柏菁 E-mail 作用:限制哪些用户或者组可以从哪里登陆,或者可以建立/etc/nologin立即阻止一般用户登陆,限制 user可以使用的资源.例如cpu,文件,登陆数量,某些服务的登陆时 ...

  5. 常见网站CSS样式重置

    腾讯 1 2 3 4 5 6 7 8 9 body,ol,ul,h1,h2,h3,h4,h5,h6,p,th,td,dl,dd,form,fieldset,legend,input,textarea, ...

  6. StringBuilder在高性能场景下的正确用法

    转载:<StringBuilder在高性能场景下的正确用法> by 江南白衣 关于StringBuilder,一般同学只简单记住了,字符串拼接要用StringBuilder,不要用+,也不 ...

  7. 使用zlib库进行目录打包

    代码很简单,具体过程就不写了. 关于加密压缩,可以看http://www.zlib.net/zlib_faq.html#faq38 中的描述,说是不支持的,但是创建的时候可以传入密码进去,不过我还没有 ...

  8. Android学习笔记(五一):服务Service(上)- IntentService

    转自 http://blog.csdn.net/flowingflying/article/details/7616333 对于需要长期运行,例如播放音乐.长期和服务器的连接,即使已不是屏幕当前的ac ...

  9. javascript promises powered by BlueBird

    什么是promises? 为什么需要promises? 参见: https://promisesaplus.com/ 使用示例: 使用promises之前,代码的编写方式: 使用promises之后: ...

  10. SNF开发平台WinForm-表单验证控件-通用

    CS程序也能做到像BS程序一样的验证效果,如下: 1.验证控件的展示 校验时如果不符合验证条件的控件,会在控件上显示较显眼的图标. 当出现不符合验证的控件时,鼠标悬浮会显示自定义的提示信息. 如:输入 ...