Mvc生命周期深度剖析
客户端发送请求->IIS, UrlRouting模块对比URL, 默认如果该URL能对应到实体文件则退出MVC管道把控制权交还给IIS.
如果RegisterRoutes中的路由规则对比成功默认情况下交给MvcRouteHandler(IRouteHandler)处理, IRouteHandler的作用是决策使用哪一个HttpHandler处理本次请求,IRouteHandler接口定义如下:
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
微软的提供的MVC框架中MvcRouteHandler实现了IRouteHandler接口,默认交给MvcHandler处理,代码如下:
public class MvcRouteHandler : IRouteHandler
{
private IControllerFactory _controllerFactory;
public MvcRouteHandler()
{
}
public MvcRouteHandler(IControllerFactory controllerFactory)
{
this._controllerFactory = controllerFactory;
}
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
{
string str = (string) requestContext.RouteData.Values["controller"];
if (string.IsNullOrWhiteSpace(str))
{
throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);
}
IControllerFactory factory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
return factory.GetControllerSessionBehavior(requestContext, str);
}
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
{
return this.GetHttpHandler(requestContext);
}
}
As we know, 所有的HttpHandler的入口点为ProcessRequest,IHttpHandler接口定义如下:
public interface IHttpHandler
{
void ProcessRequest(HttpContext context);
bool IsReusable { get; }
}
在MvcRouteHandler.ProcessRequest中首先传入HttpContext以及两个out参数到ProcessRequestInit方法中根据路由参数获取ControllerFactory类以及对应的Controller, 代码如下:
protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
IController controller;
IControllerFactory factory;
this.ProcessRequestInit(httpContext, out controller, out factory);
try
{
controller.Execute(this.RequestContext);
}
finally
{
factory.ReleaseController(controller);
}
}
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
HttpContext current = HttpContext.Current;
if ((current != null) && (ValidationUtility.IsValidationEnabled(current) == true))
{
ValidationUtility.EnableDynamicValidation(current);
}
this.AddVersionHeader(httpContext);
this.RemoveOptionalRoutingParameters();
string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
factory = this.ControllerBuilder.GetControllerFactory();
controller = factory.CreateController(this.RequestContext, requiredString);
if (controller == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] { factory.GetType(), requiredString }));
}
}
在Asp.net Mvc中所有的Controller都实现了IController接口,该接口定义了一个Execute方法,代码如下:
public interface IController
{
void Execute(RequestContext requestContext);
}
微软提供的默认框架中实现了该接口的Class为ControllerBase, ControllerBase除了实现Execute方法外还定义了一个抽象方法ExecuteCore,而实现了这个方法的就是我们工作中定义Controller时继承需要继承的System.Web.MvcController类,接上文讲,在MvcRouteHandler.ProcessRequest中取得Controller后接着会进入ControllerBase的Execute方法,代码如下:
protected virtual void Execute(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (requestContext.HttpContext == null)
{
throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
}
this.VerifyExecuteCalledOnce();
this.Initialize(requestContext);
using (ScopeStorage.CreateTransientScope())
{
this.ExecuteCore();
}
}
在做了一些验证和初始化后会进入System.Web.MvcController类的ExecuteCore方法,代码如下:
protected override void ExecuteCore()
{
this.PossiblyLoadTempData();
try
{
string requiredString = this.RouteData.GetRequiredString("action");
if (!this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString))
{
this.HandleUnknownAction(requiredString);
}
}
finally
{
this.PossiblySaveTempData();
}
}
该方法中会通过路由参数得知要运行的ActionName并通过ActionInvoker调用该Action响应客户端, 到此为止就是我们平时经常使用的Action所做的事情了,如果直接用Response客户端或返回数据本次请求的生命周期则到此结束.
若返回ViewResult还有有一些额外的操作, 通过ViewEngine获取到相应的View返回给客户端,我们先看一下IViewEngine接口的定义:
public interface IViewEngine
{
ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache);
ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache);
void ReleaseView(ControllerContext controllerContext, IView view);
}
.Net内建了两套ViewEngine,一套为WebFormViewEngine,另一套为我们比较常用的RazorViewEngine,代码如下:
public class RazorViewEngine : BuildManagerViewEngine
{
internal static readonly string ViewStartFileName = "_ViewStart";
public RazorViewEngine() : this(null)
{
}
public RazorViewEngine(IViewPageActivator viewPageActivator) : base(viewPageActivator)
{
base.AreaViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
base.AreaMasterLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
base.AreaPartialViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
base.MasterLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
base.PartialViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
base.FileExtensions = new string[] { "cshtml", "vbhtml" };
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
string layoutPath = null;
bool runViewStartPages = false;
IEnumerable<string> fileExtensions = base.FileExtensions;
return new RazorView(controllerContext, partialPath, layoutPath, runViewStartPages, fileExtensions, base.ViewPageActivator) { DisplayModeProvider = base.DisplayModeProvider };
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
string layoutPath = masterPath;
bool runViewStartPages = true;
IEnumerable<string> fileExtensions = base.FileExtensions;
return new RazorView(controllerContext, viewPath, layoutPath, runViewStartPages, fileExtensions, base.ViewPageActivator) { DisplayModeProvider = base.DisplayModeProvider };
}
}
可以看到在ViewEngine中已经内建了很多找到某个具体View文件的格式,这也是为什么我们如果不自定义MVC框架的某些模块必须严格按照微软提供的目录结构来分割文件。
Tip: 这实际上是有好处的,也是微软提倡的Convention Over Configuration(觉得没法有一个简单词概括所以就不翻译了), 意思就是他提供了一个标准的模式,大家都遵守这样一个规则,以降低不同的人维护同一个项目的复杂度: 我们知道如果每个团队甚至每个人写出的项目如果都是不同的目录结构与项目框架,如果临时换另一个团队或另一个人来接手是需要花很多时间来熟悉的, 项目越复杂也就越难以维护。
接着通过ViewEngine通过CreateView实例化一个IView对象并返回,IView接口定义如下:
public interface IView
{
void Render(ViewContext viewContext, TextWriter writer);
}
显然就是Render用于响应客户端的方法了,在RazorViewEngine中将会返回一个实现了IView接口的RazorView对象并调用Render方法最终输出页面到客户端:
protected override void RenderView(ViewContext viewContext, TextWriter writer, object instance)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
WebViewPage page = instance as WebViewPage;
if (page == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.CshtmlView_WrongViewBase, new object[] { base.ViewPath }));
}
page.OverridenLayoutPath = this.LayoutPath;
page.VirtualPath = base.ViewPath;
page.ViewContext = viewContext;
page.ViewData = viewContext.ViewData;
page.InitHelpers();
if (this.VirtualPathFactory != null)
{
page.VirtualPathFactory = this.VirtualPathFactory;
}
if (this.DisplayModeProvider != null)
{
page.DisplayModeProvider = this.DisplayModeProvider;
}
WebPageRenderingBase startPage = null;
if (this.RunViewStartPages)
{
startPage = this.StartPageLookup(page, RazorViewEngine.ViewStartFileName, this.ViewStartFileExtensions);
}
HttpContextBase httpContext = viewContext.HttpContext;
WebPageRenderingBase base4 = null;
object model = null;
page.ExecutePageHierarchy(new WebPageContext(httpContext, base4, model), writer, startPage);
}
至此完整的生命周期介绍完毕,实际上关于后面的ViewEngine部分只是粗浅的介绍了一下运转流程,关于具体细节需要更大的篇幅来介绍在此就不再展开,目前除了微软内建的ViewEngine外, 也有很多优秀的第三方ViewEngine可供大家参考比如SparkViewEngine、NDjango、NHaml等.
由于个人水平有限,理解有误的地方恳请斧正!
Mvc生命周期深度剖析的更多相关文章
- ASP.NET MVC 生命周期
本文的目的旨在详细描述ASP.NET MVC请求从开始到结束的每一个过程.我希望能理解在浏览器输入URL并敲击回车来请求一个ASP.NET MVC网站的页面之后发生的任何事情. 为什么需要关心这些?有 ...
- ASP.NET MVC生命周期介绍(转)
本文以IIS7中asp.net应用程序生命周期为例,介绍了asp.net mvc的生命周期. asp.net应用程序管道处理用户请求时特别强调"时机",对asp.net生命周期的了 ...
- Asp.net MVC生命周期
Asp.net应用程序管道处理用户请求时特别强调"时机",对Asp.net生命周期的了解多少直接影响我们写页面和控件的效率.因此在2007年和2008年我在这个话题上各写了一篇文章 ...
- MVC学习笔记---MVC生命周期
Asp.net应用程序管道处理用户请求时特别强调"时机",对Asp.net生命周期的了解多少直接影响我们写页面和控件的效率.因此在2007年和2008年我在这个话题上各写了一篇文章 ...
- MVC学习笔记---MVC生命周期及管道
ASP.NET和ASP.NET MVC的HttpApplication请求处理管道有共同的部分和不同之处,本系列将体验ASP.NET MVC请求处理管道生命周期的19个关键环节. ①以IIS6.0为例 ...
- [收藏]Asp.net MVC生命周期
一个HTTP请求从IIS移交到Asp.net运行时,Asp.net MVC是在什么时机获得了控制权并对请求进行处理呢?处理过程又是怎样的? 以IIS7中asp.net应用程序生命周期为例,下图是来自M ...
- 1.3 ASP.NET MVC生命周期
ASP.NET MVC的执行生命周期主要分为三个阶段,分别是网址路由对比.执行控制器与动作.执行视图并返回结果.从ASP.NET MVC接受HTTP请求到返回HTTP响应的过程如下图所示.
- asp.net mvc生命周期学习
ASP.NET MVC是一个扩展性非常强的框架,探究其生命周期对用Mock框架来模拟某些东西,达到单元测试效果,和开发扩展我们的程序是很好的. 生命周期1:创建routetable.把URL映射到ha ...
- 【转】.net MVC 生命周期
对于Asp.net MVC,我对它的生命周期还是兴趣很浓,于是提出两个问题: 一个HTTP请求从IIS移交到Asp.net运行时,Asp.net MVC是在什么时机获得了控制权并对请求进行处理呢?处理 ...
随机推荐
- 给jdk写注释系列之jdk1.6容器(4)-HashMap源码解析
前面了解了jdk容器中的两种List,回忆一下怎么从list中取值(也就是做查询),是通过index索引位置对不对,由于存入list的元素时安装插入顺序存储的,所以index索引也就是插入的次序. M ...
- java通过反射获取调用变量以及方法
一:反射概念 可以通过Class类获取某个类的成员变量以及方法,并且调用之. 二:通过反射获取方法.变量.构造方法 @Test // 通过反射获取类定义的方法 public void testMeth ...
- Adobe Edge Animate --使用HTML5实现手机摇一摇功能
Adobe Edge Animate --使用HTML5实现手机摇一摇功能 版权声明: 本文版权属于 北京联友天下科技发展有限公司. 转载的时候请注明版权和原文地址. HTML5的发展日新月异,其功能 ...
- cvSaveImage的第三个参数
http://stackoverflow.com/questions/801054/opencv-cvsaveimage-jpeg-compression-factor #define CV_IM ...
- git 一个文件还原到某个提交的commit
git checkout ${commit} /path/to/file 参考文献中1的参考链接中的git-checkout(1) Manual Page 中的Name: git-checkout - ...
- sql常识-like
LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式. LIKE 操作符 LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式. SQL LIKE 操作符语法 SELECT colum ...
- asp.net select Case条件语句的使用方法
原文:http://www.111cn.net/net/vb-net/38548.htm 如果 testexpression 与任何 Case expressionlist 表达式匹配 ,则执行此 C ...
- ubuntu 更新软件源
ubuntu 更新软件源 修改文件sources.list 位于/etc/apt/sources.list,并备份原文件为sources.list.bak deb http://mirrors.163 ...
- 【转载】 c语言inline函数的使用
c语言inline函数的使用 转载自:http://blog.chinaunix.net/uid-21843265-id-3056446.html 大学在教科书上学习过inline函数,定义为inli ...
- javascript笔记——JavaScript经典实例
转载自百度文库 http://wenku.baidu.com/view/9a703522bcd126fff7050bfa.html 1. oncontextmenu="window.even ...