做ASP.NET WebForm开发都知道,ASP.NET有复杂的生命周期,学习ASP.NET MVC就要深入理解它的生命周期。今天从CodePlex上下载了ASP.NET Preview 2 的源代码,还有两个程序集Routing与Abstractions并未发布,不过这两个程序集的类并不多,可以用NET反编译工具 Reflector解开来看看,可惜这两个程序集用的是VS2008使用.net 3.5开发的,用了c# 3.0的很多特性,Reflector反编译不完全。

ASP.NET MVC通过HttpModule(UrlRoutingModule)开始他的执行流程

ASP.NET MVC Preview生命周期分析<httpModules>
ASP.NET MVC Preview生命周期分析     
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing" />
ASP.NET MVC Preview生命周期分析
</httpModules> 

代码如下:

ASP.NET MVC Preview生命周期分析namespace System.Web.Routing

里面还定义了一个RequestData,主要就是当前处理的HttpHandler和URL原始路径。来看看ASP.NET 的HttpApplication 管线会依次处理下面的请求:

  1. 对请求进行验证,将检查浏览器发送的信息,并确定其是否包含潜在恶意标记。

  2. 如果已在 Web.config 文件的 UrlMappingsSection 节中配置了任何 URL,则执行 URL 映射。

  3. 引发 BeginRequest 事件。

  4. 引发 AuthenticateRequest 事件。

  5. 引发 PostAuthenticateRequest 事件。

  6. 引发 AuthorizeRequest 事件。

  7. 引发 PostAuthorizeRequest 事件。

  8. 引发 ResolveRequestCache 事件。

  9. 引发 PostResolveRequestCache 事件。

  10. 根据所请求资源的文件扩展名(在应用程序的配置文件中映射),选择实现 IHttpHandler 的类,对请求进行处理。如果该请求针对从 Page 类派生的对象(页),并且需要对该页进行编译,则 ASP.NET 会在创建该页的实例之前对其进行编译。

  11. 引发 PostMapRequestHandler 事件。

  12. 引发 AcquireRequestState 事件。

  13. 引发 PostAcquireRequestState 事件。

  14. 引发 PreRequestHandlerExecute 事件。

  15. 为该请求调用合适的 IHttpHandler 类的 ProcessRequest 方法(或异步版 BeginProcessRequest)。例如,如果该请求针对某页,则当前的页实例将处理该请求。

  16. 引发 PostRequestHandlerExecute 事件。

  17. 引发 ReleaseRequestState 事件。

  18. 引发 PostReleaseRequestState 事件。

  19. 如果定义了 Filter 属性,则执行响应筛选。

  20. 引发 UpdateRequestCache 事件。

  21. 引发 PostUpdateRequestCache 事件。

  22. 引发 EndRequest 事件。

上述UrlRoutingModule订阅了两个 HttpApplication 事件,PostResolveRequestCache 要比 PostMapRequestHandler 更早执行,先看PostResolveRequestCache 事件,他首先从首先从 RouteCollection 中获取一个 RouteData 对象。看看RouteCollection 属性:

ASP.NET MVC Preview生命周期分析    public System.Web.Routing.RouteCollection RouteCollection

看到了来自RouteTable,这不正是在Global.asax.cs 中添加的 Route 集合:

ASP.NET MVC Preview生命周期分析  protected void Application_Start(object sender, EventArgs e)

 上述代码来自[翻译]使用asp.net mvc再造一个digg 第一部分的kigg。回到上文,在获取 RoteCollection 之后,通过调用 GetRouteData(context) 返回一个 RouteData 对象,该对象内部包含了我们注册 Route 时的相关设置,包括下面所需要的 MvcRouteHandler。接下来,该方法将 routeData 和上下文一起打包成 RequestContext,这就是为相关处理准备的上下文环境。通过调用 IRouteHandler.GetHttpHandler() 方法,终于到达流程的关键IHttpHandler(MvcHandler)。在WebForm中我们知道每一个页面都是一个HttpHandler,Asp.net mvc也不例外。

先来看看MvcRouteHandler:

 

 

这里得到了MvcHandler:

 

}
到了这一步,MVC 框架已经准备好了相应的执行场景,接下来就是修改默认(指WebForm)的执行流程了。
ASP.NET MVC Preview生命周期分析             RequestData data2 = new RequestData();
ASP.NET MVC Preview生命周期分析                data2.OriginalPath 
= context.Request.Path;
ASP.NET MVC Preview生命周期分析                data2.HttpHandler 
= httpHandler;
ASP.NET MVC Preview生命周期分析                context.Items[_requestDataKey] 
= data2;
ASP.NET MVC Preview生命周期分析                context.RewritePath(
"~/UrlRouting.axd"); 
ASP.NET MVC Preview生命周期分析

OnApplicationPostMapRequestHandler 被执行。在 PostMapRequestHandler 中,它提取了前面预先准备好的上下文,并修改了 HttpContext.Handler,使得 MvcHandler 接管默认的WebForm的HttpHandler,才是执行ASP.NET MVC的流程。现在来继续看MvcHandler。

首先从 RouteData 中提取 Controller 的名字(这个名字是我们在 Global.asax.cs RegisterRoutes 中注册 Route 时提供的),然后获取 ControllerFactory,只不过这里面专门提供了一个 ControllerBuilder。

 

如果我们自定义MvcHandler,则需要好好的看看ControllerBuilder.Current:

 

(1) 通常情况下,返回一个默认的 DefaultControllerFactory 实例。
(2) 我们可以在 Application_Start 中通过 ControllerBuilder.Current.SetControllerFactory 方法来注册一个我们自定义的工厂。
(3) 核心代码: factory = (IControllerFactory)Activator.CreateInstance(controllerFactoryType),通过反射创建IControllerFactory;

回到MvcHandler的 ProcessRequest ,DefaultControllerFactory.CreateController(RequestContext, requiredString) 来返回 IController 实例。下面看看DefaultControllerFactory的代码:

通过反射来创建 Controller 实例,GetControllerType 里面做了些缓存处理,以此来避免频繁使用反射造成的性能问题。继续

MvcHandler.ProcessRequest(),在得到控制器实例后,MvcHandler 开始了调用 Controller.Execute() 来进一步后续操作,同时对其上下文进一步封装,除了前面创建的 RequestContext,还加上了当前这个 Controller 对象的引用,类名叫ControllerContext。

 

 

继续看IController的默认实现类Controller:

 

 

获取 Action 的名字,然后开始执行 InvokeAction,如果找不到Action,则调用HandleUnknownAction,这是一个虚拟方法,可以在子类中重写,默认是抛出一个异常InvalidOperationException。

 

详细看看InvokeAction方式的执行:

 

 

它通过反射获取所有同名 Action 方法信息;其次,它过滤掉所有有 NonActionAttribute 和 IsSpecialName 标记的方法;第三,当同名有效 Action 被重载时它会抛出异常(提示Controller_MoreThanOneAction),继续调用InvokeActionMethod:

ASP.NET MVC Preview生命周期分析[SuppressMessage("Microsoft.Design""CA1011:ConsiderPassingBaseTypesAsParameters",
ASP.NET MVC Preview生命周期分析     Justification 
= "We use MethodInfo since it represents only methods and not constructors." +
ASP.NET MVC Preview生命周期分析     
"This method only makes sense for use with methods.")]
}
这个方法很复杂,有大量的代码是参数的分解 ,只有最后一行是关键的。

InvokeActionMethodFilters(methodInfo, () => methodInfo.Invoke(this, parameterValues));

这行代码将 Action 的调用作为一个委托,连同反射信息传递给 InvokeActionMethodFilters。

 

 

这个方法首先将默认的过滤器 ControllerActionFilter 加到列表,然后提取所有继承层次上基类的过滤器特性。最后将这些过滤器集合、过滤上下文,连同前一个方法传递进来的 Action 执行委托(continuation) 再次转交给了一个 ActionFilterExecutor 对象实例,并调用其 Execute 方法。

ExecuteRecursive使用了递归算法,通过迭代器 MoveNext() 方法提取一个过滤器对象,执行其 OnActionExecuting 方法。 如果该方法设置了 filterContext.Cancel = true,则放弃后续执行代码。这种机制为我们提供了更好的控制,例如用它来实现伪静态页。一层一层调用所有的 ActionFilterAttribute.OnActionExecuting 方法,直到 MoveNext() == false。 在最后一次递归调用时,由于 enumerator.MoveNext() == false, _continuation() 方法被执行。这个就是前面给传递过来的 Action 方法委托,Action 方法总算是执行了。 在 Action 委托执行完成后,递归调逐级往上回溯,直到最初那个方法堆栈。这样所有ActionFilterAttribute.OnActionExecuted 也被执行完成。

到此开始进入最后的视图呈现阶段,可以把数据呈现到视图上,Controller 提供了几个重载的 RenderView() 来完成这个工作。

 

将一路传递过来的相关 "数据" (上下文)ControllerContext 再次包装成ViewContext 。当然,这次依然会多出些东西,里面就有我们向视图传递的数据 —— viewData 和tempData。作为默认选择,MVC 创建 WebForm 视图引擎来展示结果。其他的视图引擎可以去看mvccontrib,这个项目就是整合:Castle WindsorStructureMapSpring.NET 等IoC框架以及视图引擎,包括Castle MonoRail所用的NVelocityView视图引擎、NHamlView视图引擎、XsltViewEngine视图引擎等等。

继续看这个 WebFormViewEngine:

ASP.NET MVC Preview生命周期分析
ASP.NET MVC Preview生命周期分析
 

首先会创建一个 WebFormViewLocator 对象来获取视图存放路径。

namespace System.Web.Mvc {
    public class WebFormViewLocator : ViewLocator {
        public WebFormViewLocator() {
            ViewLocationFormats = new[] {
                "~/Views/{1}/{0}.aspx",
                "~/Views/{1}/{0}.ascx",
                "~/Views/Shared/{0}.aspx",
                "~/Views/Shared/{0}.ascx"
            };
            MasterLocationFormats = new[] {
                "~/Views/{1}/{0}.master",
                "~/Views/Shared/{0}.master"
            };
        }
    }
}

在获取路径后,WebFormViewEngine 通过一个包装类 BuildManagerWrapper 间接调用 System.Web.Compilation.BuildManager 的静态方法 CreateInstanceFromVirtualPath() 将视图进行编译,并返回一个对象实例。(System.Web.Compilation.BuildManager BuildManager 类管理应用程序的程序集和页的编译过程),后面通过 as 转换结果来判断视图是 ViewPage 还是 ViewUserControl。ViewPage 继承自我们所熟悉的 System.Web.UI.Page,它的 RenderView() 方法也就是完成WebForm的 Page.ProcessRequest() 的处理而已,也就完成了对WebForm模型的置换。

下面看看ViewPage和ViewUserControl的代码:

namespace System.Web.Mvc {
    using System.Web.UI;

    [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    public class ViewPage : Page, IViewDataContainer {
        private string _masterLocation;
        private object _viewData;

        public string MasterLocation {
            get {
                return _masterLocation ?? String.Empty;
            }
            set {
                _masterLocation = value;
            }
        }

        public AjaxHelper Ajax {
            get;
            set;
        }

        public HtmlHelper Html {
            get;
            set;
        }

        public TempDataDictionary TempData {
            get {
                return ViewContext.TempData;
            }
        }

        public UrlHelper Url {
            get;
            set;
        }

        public ViewContext ViewContext {
            get;
            private set;
        }

        public ViewData ViewData {
            get {
                return new ViewData(_viewData);
            }
        }

        public HtmlTextWriter Writer {
            get;
            private set;
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
        protected override void OnPreInit(EventArgs e) {
            base.OnPreInit(e);

            if (!String.IsNullOrEmpty(MasterLocation)) {
                MasterPageFile = MasterLocation;
            }
        }

        public virtual void RenderView(ViewContext viewContext) {
            ViewContext = viewContext;
            Ajax = new AjaxHelper(viewContext);
            Html = new HtmlHelper(viewContext);
            Url = new UrlHelper(viewContext);

            ProcessRequest(HttpContext.Current);
        }

        protected override void Render(HtmlTextWriter writer) {
            Writer = writer;
            try {
                base.Render(writer);
            }
            finally {
                Writer = null;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
            Justification = "There is already a ViewData property and it has a slightly different meaning.")]
        protected internal virtual void SetViewData(object viewData) {
            _viewData = viewData;
        }

        #region IViewDataContainer Members
        object IViewDataContainer.ViewData {
            get {
                return _viewData;
            }
        }
        #endregion
    }
}

 

namespace System.Web.Mvc {
    using System.ComponentModel;
    using System.Globalization;
    using System.Web.Resources;
    using System.Web.UI;
    using System.Web.Mvc.Resources;

    [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    public class ViewUserControl : UserControl, IViewDataContainer {
        private string _viewDataKey;
        private object _viewData;

        public AjaxHelper Ajax {
            get {
                return ViewPage.Ajax;
            }
        }

        public HtmlHelper Html {
            get {
                return ViewPage.Html;
            }
        }

        public TempDataDictionary TempData {
            get {
                return ViewPage.TempData;
            }
        }

        public UrlHelper Url {
            get {
                return ViewPage.Url;
            }
        }

        public ViewData ViewData {
            get {
                EnsureViewData();
                return new ViewData(_viewData);
            }
        }

        public ViewContext ViewContext {
            get {
                return ViewPage.ViewContext;
            }
        }

        [DefaultValue("")]
        public string ViewDataKey {
            get {
                return _viewDataKey ?? String.Empty;
            }
            set {
                _viewDataKey = value;
            }
        }

        private ViewPage ViewPage {
            get {
                ViewPage viewPage = Page as ViewPage;
                if (viewPage == null) {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, MvcResources.ViewUserControl_RequiresViewPage));
                }
                return viewPage;
            }
        }

        public HtmlTextWriter Writer {
            get {
                return ViewPage.Writer;
            }
        }

        private void EnsureViewData() {
            // Get the ViewData for this ViewUserControl, optionally using the specified ViewDataKey
            if (_viewData != null) {
                return;
            }
            IViewDataContainer vdc = GetViewDataContainer(this);
            if (vdc == null) {
                throw new InvalidOperationException(
                    String.Format(
                        CultureInfo.CurrentUICulture,
                        MvcResources.ViewUserControl_RequiresViewDataProvider,
                        AppRelativeVirtualPath));
            }
            if (String.IsNullOrEmpty(ViewDataKey)) {
                _viewData = vdc.ViewData;
            }
            else {
                _viewData = DataBinder.Eval(vdc.ViewData, ViewDataKey);
            }
        }

        private static IViewDataContainer GetViewDataContainer(Control control) {
            // Walk up the control hierarchy until we find someone that implements IViewDataContainer
            while (control != null) {
                control = control.Parent;
                IViewDataContainer vdc = control as IViewDataContainer;
                if (vdc != null) {
                    return vdc;
                }
            }
            return null;
        }

        public virtual void RenderView(ViewContext viewContext) {
            // TODO: Remove this hack. Without it, the browser appears to always load cached output
            viewContext.HttpContext.Response.Cache.SetExpires(DateTime.Now);
            ViewUserControlContainerPage containerPage = new ViewUserControlContainerPage(this);
            containerPage.RenderView(viewContext);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
           Justification = "There is already a ViewData property and it has a slightly different meaning.")]
        protected internal virtual void SetViewData(object viewData) {
            _viewData = viewData;
        }

        #region IViewDataContainer Members
        object IViewDataContainer.ViewData {
            get {
                EnsureViewData();
                return _viewData;
            }
        }
        #endregion

        private sealed class ViewUserControlContainerPage : ViewPage {
            public ViewUserControlContainerPage(ViewUserControl userControl) {
                Controls.Add(userControl);
            }
        }
    }
}

以直接在 Controller 中 RenderView 一个用户控件(ViewUserControl),asp.net mvc 会替我们创建了一个 "空白页" (ViewUserControlContainerPage )来装载这个控件RenderView(ViewUserControl) 有个限制,就是不能有 MasterPage。

    private sealed class ViewUserControlContainerPage : ViewPage {
           public ViewUserControlContainerPage(ViewUserControl userControl) {
               Controls.Add(userControl);
           }
       }

我们从 UrlRoutingModule 开始,历经 MvcRouteHandler、MvcHandler、Controller、ActionFilterAttribute,直到最后的 ViewEngine、ViewPage.完成了整个ASP.NET MVC的生命周期探索。

相关文章:

  • 2021-07-04
  • 2022-01-10
  • 2021-08-26
  • 2021-12-04
  • 2021-06-15
  • 2021-09-26
猜你喜欢
  • 2021-12-31
  • 2021-05-31
  • 2021-10-06
  • 2021-12-01
相关资源
相似解决方案