做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)开始他的执行流程
<httpModules>
    <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing" />
</httpModules>
代码如下:
namespace System.Web.Routing
{
    using System;
    using System.Globalization;
    using System.Runtime.CompilerServices;
    using System.Security.Permissions;
    using System.Web;
    using System.Web.Resources; 
  [AspNetHostingPermission(SecurityAction.InheritanceDemand,Level=AspNetHostingPermissionLevel.Minimal),AspNetHostingPermission(SecurityAction.LinkDemand,Level=AspNetHostingPermissionLevel.Minimal)]
    public class UrlRoutingModule : IHttpModule
    {
        private static readonly object _requestDataKey = new object();
        private System.Web.Routing.RouteCollection _routeCollection; 
        protected virtual void Dispose()
        {
        } 
        protected virtual void Init(HttpApplication application)
        {
            application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
            application.PostMapRequestHandler += new EventHandler(this.OnApplicationPostMapRequestHandler);
        } 
        private void OnApplicationPostMapRequestHandler(object sender, EventArgs e)
        {
            HttpContextBase context = new HttpContextWrapper2(((HttpApplication) sender).Context);
            this.PostMapRequestHandler(context);
        } 
        private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
        {
            HttpContextBase context = new HttpContextWrapper2(((HttpApplication) sender).Context);
            this.PostResolveRequestCache(context);
        } 
        public virtual void PostMapRequestHandler(HttpContextBase context)
        {
            RequestData data = (RequestData) context.Items[_requestDataKey];
            if (data != null)
            {
                context.RewritePath(data.OriginalPath);
                context.Handler = data.HttpHandler;
            }
        } 
        public virtual void PostResolveRequestCache(HttpContextBase context)
        {
            RouteData routeData = this.RouteCollection.GetRouteData(context);
            if (routeData != null)
            {
                IRouteHandler routeHandler = routeData.RouteHandler;
                if (routeHandler == null)
                {
                  throw newInvalidOperationException(string.Format(CultureInfo.CurrentUICulture,RoutingResources.UrlRoutingModule_NoRouteHandler, new object[0]));
                }
                RequestContext requestContext = new RequestContext(context, routeData);
                IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
                if (httpHandler == null)
                {
                  throw newInvalidOperationException(string.Format(CultureInfo.CurrentUICulture,RoutingResources.UrlRoutingModule_NoHttpHandler, new object[] {routeHandler.GetType() }));
                }
                RequestData data2 = new RequestData();
                data2.OriginalPath = context.Request.Path;
                data2.HttpHandler = httpHandler;
                context.Items[_requestDataKey] = data2;
                context.RewritePath("~/UrlRouting.axd");
            }
        } 
        void IHttpModule.Dispose()
        {
            this.Dispose();
        } 
        void IHttpModule.Init(HttpApplication application)
        {
            this.Init(application);
        } 
        public System.Web.Routing.RouteCollection RouteCollection
        {
            get
            {
                if (this._routeCollection == null)
                {
                    this._routeCollection = RouteTable.Routes;
                }
                return this._routeCollection;
            }
            set
            {
                this._routeCollection = value;
            }
        }

}
里面还定义了一个RequestData,主要就是当前处理的HttpHandler和URL原始路径。来看看ASP.NET 的HttpApplication 管线会依次处理下面的请求:
  • 对请求进行验证,将检查浏览器发送的信息,并确定其是否包含潜在恶意标记。
  • 如果已在 Web.config 文件的 UrlMappingsSection 节中配置了任何 URL,则执行 URL 映射。
  • 引发 BeginRequest 事件。
  • 引发 AuthenticateRequest 事件。
  • 引发 PostAuthenticateRequest 事件。
  • 引发 AuthorizeRequest 事件。
  • 引发 PostAuthorizeRequest 事件。
  • 引发 ResolveRequestCache 事件。
  • 引发 PostResolveRequestCache 事件。
  • 根据所请求资源的文件扩展名(在应用程序的配置文件中映射),选择实现 IHttpHandler 的类,对请求进行处理。如果该请求针对从 Page 类派生的对象(页),并且需要对该页进行编译,则 ASP.NET 会在创建该页的实例之前对其进行编译。
  • 引发 PostMapRequestHandler 事件。
  • 引发 AcquireRequestState 事件。
  • 引发 PostAcquireRequestState 事件。
  • 引发 PreRequestHandlerExecute 事件。
  • 为该请求调用合适的 IHttpHandler 类的 ProcessRequest 方法(或异步版 BeginProcessRequest)。例如,如果该请求针对某页,则当前的页实例将处理该请求。
  • 引发 PostRequestHandlerExecute 事件。
  • 引发 ReleaseRequestState 事件。
  • 引发 PostReleaseRequestState 事件。
  • 如果定义了 Filter 属性,则执行响应筛选。
  • 引发 UpdateRequestCache 事件。
  • 引发 PostUpdateRequestCache 事件。
  • 引发 EndRequest 事件。
上述UrlRoutingModule订阅了两个 HttpApplication 事件,PostResolveRequestCache 要比PostMapRequestHandler 更早执行,先看PostResolveRequestCache 事件,他首先从首先从RouteCollection 中获取一个 RouteData 对象。看看RouteCollection 属性:
        public System.Web.Routing.RouteCollection RouteCollection
        {
            get
            {
                if (this._routeCollection == null)
                {
                    this._routeCollection = RouteTable.Routes;
                }
                return this._routeCollection;
            }
            set
            {
                this._routeCollection = value;
            }
        }
看到了来自RouteTable,这不正是在Global.asax.cs 中添加的 Route 集合:
      protected void Application_Start(object sender, EventArgs e)
      {
          RegisterRoutes(RouteTable.Routes);
          CreateDefaultUserIfNotExists();
      } 
      public static void RegisterRoutes(RouteCollection routes)
      {
          int iisVersion =Convert.ToInt32(ConfigurationManager.AppSettings["IISVersion"],System.Globalization.CultureInfo.InvariantCulture);
          if (iisVersion >= 7)
          {
              RegisterRoutesForNewIIS(routes);
          }
          else
          {
              RegisterRoutesForOldIIS(routes);
          }
      } 
      private static void RegisterRoutesForNewIIS(ICollection<RouteBase> routes)
      {
          routes.Add(new Route("User/Login", new RouteValueDictionary(new {c, action = "Login" }), new MvcRouteHandler()));
          routes.Add(new Route("User/Logout", new RouteValueDictionary(new {c, action = "Logout" }), new MvcRouteHandler()));
          routes.Add(new Route("User/Signup", new RouteValueDictionary(new {c, action = "Signup" }), new MvcRouteHandler()));
          routes.Add(new Route("User/SendPassword", new RouteValueDictionary(new{ c, action = "SendPassword" }), newMvcRouteHandler()));
            routes.Add(newRoute("Story/Detail/{id}", new RouteValueDictionary(new { c, action = "Detail" }), new MvcRouteHandler()));
          routes.Add(new Route("Story/Upcoming/{page}", newRouteValueDictionary(new { c, action = "Upcoming"}), new MvcRouteHandler()));
          routes.Add(newRoute("Story/Search/{q}/{page}", new RouteValueDictionary(new {c, action = "Search" }), new MvcRouteHandler()));
          var defaults = new RouteValueDictionary (
                                                      new
                                                      {
                                                          c,
                                                          action = "Category",
                                                          name = (string)null,
                                                          page = (int?)null
                                                      }
                                                  ); 
          routes.Add(new Route("Story/Category/{page}", defaults, new MvcRouteHandler()));
          routes.Add(new Route("Story/{action}/{name}/{page}", defaults, new MvcRouteHandler()));
          routes.Add(new Route("{controller}/{action}/{id}", defaults, new MvcRouteHandler()));
          routes.Add(new Route("Default.aspx", defaults, new MvcRouteHandler()));
      } 
      private static void RegisterRoutesForOldIIS(ICollection<RouteBase> routes)
      {
          routes.Add(new Route("User.mvc/Login", new RouteValueDictionary(new {c, action = "Login" }), new MvcRouteHandler()));
          routes.Add(new Route("User.mvc/Logout", new RouteValueDictionary(new {c, action = "Logout" }), new MvcRouteHandler()));
          routes.Add(new Route("User.mvc/Signup", new RouteValueDictionary(new {c, action = "Signup" }), new MvcRouteHandler()));
          routes.Add(new Route("User.mvc/SendPassword", newRouteValueDictionary(new { c, action = "SendPassword"}), new MvcRouteHandler()));
          routes.Add(newRoute("Story.mvc/Detail/{id}", new RouteValueDictionary(new {c, action = "Detail" }), new MvcRouteHandler()));
          routes.Add(new Route("Story.mvc/Upcoming/{page}", newRouteValueDictionary(new { c, action = "Upcoming"}), new MvcRouteHandler()));
          routes.Add(newRoute("Story.mvc/Search/{q}/{page}", new RouteValueDictionary(new {c, action = "Search" }), new MvcRouteHandler()));
          var defaults = new RouteValueDictionary(
                                                      new
                                                      {
                                                          c,
                                                          action = "Category",
                                                          name = (string)null,
                                                          page = (int?)null
                                                      }
                                                  ); 
          routes.Add(new Route("Story.mvc/Category/{page}", defaults, new MvcRouteHandler()));
          routes.Add(new Route("Story.mvc/{action}/{name}/{page}", defaults, new MvcRouteHandler()));
          routes.Add(new Route("{controller}.mvc/{action}/{id}", defaults, new MvcRouteHandler()));
          routes.Add(new Route("Default.aspx", defaults, new MvcRouteHandler()));
      }
上述代码来自[翻译]使用asp.net mvc再造一个digg 第一部分的kigg。回到上文,在获取 RoteCollection 之后,通过调用 GetRouteData(context) 返回一个 RouteData对象,该对象内部包含了我们注册 Route 时的相关设置,包括下面所需要的 MvcRouteHandler。接下来,该方法将routeData 和上下文一起打包成 RequestContext,这就是为相关处理准备的上下文环境。通过调用IRouteHandler.GetHttpHandler()方法,终于到达流程的关键IHttpHandler(MvcHandler)。在WebForm中我们知道每一个页面都是一个HttpHandler,Asp.net mvc也不例外。
先来看看MvcRouteHandler:
namespace System.Web.Mvc {
    using System.Web.Routing; 
  [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
  [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
    public class MvcRouteHandler : IRouteHandler {
        protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
            return new MvcHandler(requestContext);
        } 
        #region IRouteHandler Members
        IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) {
            return GetHttpHandler(requestContext);
        }
        #endregion
    }
}
这里得到了MvcHandler:
namespace System.Web.Mvc {
    using System.Globalization;
    using System.Web.Mvc.Resources;
    using System.Web.Routing;
    using System.Web.SessionState; 
  [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
  [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
    public class MvcHandler : IHttpHandler, IRequiresSessionState {
        private ControllerBuilder _controllerBuilder; 
        public MvcHandler(RequestContext requestContext) {
            if (requestContext == null) {
                throw new ArgumentNullException("requestContext");
            }
            RequestContext = requestContext;
        } 
        protected virtual bool IsReusable {
            get {
                // REVIEW: What's this?
                return false;
            }
        } 
        internal ControllerBuilder ControllerBuilder {
            get {
                if (_controllerBuilder == null) {
                    _controllerBuilder = ControllerBuilder.Current;
                }
                return _controllerBuilder;
            }
            set {
                _controllerBuilder = value;
            }
        } 
        public RequestContext RequestContext {
            get;
            private set;
        } 
        protected virtual void ProcessRequest(HttpContext httpContext) {
            HttpContextBase iHttpContext = new HttpContextWrapper2(httpContext);
            ProcessRequest(iHttpContext);
        } 
        protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
            // Get the controller type
            string controllerName = RequestContext.RouteData.GetRequiredString("controller"); 
            // Instantiate the controller and call Execute
            IControllerFactory 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 {
                ControllerContext controllerContext = new ControllerContext(RequestContext, controller);
                controller.Execute(controllerContext);
            }
            finally {
                factory.DisposeController(controller);
            }
        } 
        #region IHttpHandler Members
        bool IHttpHandler.IsReusable {
            get {
                return IsReusable;
            }
        } 
        void IHttpHandler.ProcessRequest(HttpContext httpContext) {
            ProcessRequest(httpContext);
        }
        #endregion
    }
}
到了这一步,MVC 框架已经准备好了相应的执行场景,接下来就是修改默认(指WebForm)的执行流程了。                RequestData data2 = new RequestData();
                data2.OriginalPath = context.Request.Path;
                data2.HttpHandler = httpHandler;
                context.Items[_requestDataKey] = data2;
                context.RewritePath("~/UrlRouting.axd");
OnApplicationPostMapRequestHandler被执行。在 PostMapRequestHandler 中,它提取了前面预先准备好的上下文,并修改了HttpContext.Handler,使得 MvcHandler 接管默认的WebForm的HttpHandler,才是执行ASP.NETMVC的流程。现在来继续看MvcHandler。
首先从 RouteData 中提取 Controller的名字(这个名字是我们在 Global.asax.cs RegisterRoutes 中注册 Route 时提供的),然后获取ControllerFactory,只不过这里面专门提供了一个 ControllerBuilder。
internal ControllerBuilder ControllerBuilder {
            get {
                if (_controllerBuilder == null) {
                    _controllerBuilder = ControllerBuilder.Current;
                }
                return _controllerBuilder;
            }
            set {
                _controllerBuilder = value;
            }
        }
如果我们自定义MvcHandler,则需要好好的看看ControllerBuilder.Current:
namespace System.Web.Mvc {
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Web;
    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 ControllerBuilder {
        private static ControllerBuilder _instance = new ControllerBuilder();
        private Func<IControllerFactory> _factoryThunk; 
        public ControllerBuilder() {
            SetControllerFactory(new DefaultControllerFactory());
        } 
        public static ControllerBuilder Current {
            get {
                return _instance;
            }
        } 
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
            Justification = "Calling method multiple times might return different objects.")]
        public IControllerFactory GetControllerFactory() {
            IControllerFactory controllerFactoryInstance = _factoryThunk();
            return controllerFactoryInstance;
        } 
        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);
                }
            };
        }
    }
}
(1) 通常情况下,返回一个默认的 DefaultControllerFactory 实例。
(2) 我们可以在 Application_Start 中通过 ControllerBuilder.Current.SetControllerFactory 方法来注册一个我们自定义的工厂。
(3) 核心代码: factory = (IControllerFactory)Activator.CreateInstance(controllerFactoryType),通过反射创建IControllerFactory;
回到MvcHandler的 ProcessRequest,DefaultControllerFactory.CreateController(RequestContext,requiredString) 来返回 IController 实例。下面看看DefaultControllerFactory的代码:
namespace System.Web.Mvc {
    using System;
    using System.Collections;
    using System.Globalization;
    using System.Reflection;
    using System.Web;
    using System.Web.Mvc.Resources;
    using System.Web.Routing; 
  [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
  [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
    public class DefaultControllerFactory : IControllerFactory {
        private static object _controllerTypeCacheLock = new object();
        private static ControllerTypeCache _controllerTypeCache = new ControllerTypeCache();
        private ControllerTypeCache _currentControllerTypeCache;
        private IBuildManager _buildManager; 
        internal IBuildManager BuildManager {
            get {
                if (_buildManager == null) {
                    _buildManager = new BuildManagerWrapper();
                }
                return _buildManager;
            }
            set {
                _buildManager = value;
            }
        } 
        internal ControllerTypeCache ControllerTypeCache {
            get {
                if (_currentControllerTypeCache == null) {
                    _currentControllerTypeCache = _controllerTypeCache;
                }
                return _currentControllerTypeCache;
            }
            set {
                _currentControllerTypeCache = value;
            }
        } 
        public RequestContext RequestContext {
            get;
            set;
        } 
        protected internal 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;
        } 
        protected internal virtual void DisposeController(IController controller) {
            IDisposable disposable = controller as IDisposable;
            if (disposable != null) {
                disposable.Dispose();
            }
        } 
        protected internal virtual IController GetControllerInstance(Type controllerType) {
            if (controllerType == null) {
                throw new ArgumentNullException(
                    "controllerType",
                    String.Format(
                        CultureInfo.CurrentUICulture,
                        MvcResources.DefaultControllerFactory_NoControllerFound,
                        RequestContext.HttpContext.Request.Path));
            }
            if (!typeof(IController).IsAssignableFrom(controllerType)) {
                throw new ArgumentException(
                    String.Format(
                        CultureInfo.CurrentUICulture,
                        MvcResources.DefaultControllerFactory_MissingIController,
                        controllerType),
                    "controllerType");
            }
            try {
                return Activator.CreateInstance(controllerType) as IController;
            }
            catch (Exception ex) {
                throw new InvalidOperationException(
                    String.Format(
                        CultureInfo.CurrentUICulture,
                        MvcResources.DefaultControllerFactory_ErrorCreatingController,
                        controllerType),
                    ex);
            }
        } 
        protected internal virtual Type GetControllerType(string controllerName) {
            if (String.IsNullOrEmpty(controllerName)) {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
            } 
            if (!ControllerTypeCache.Initialized) {
                lock (_controllerTypeCacheLock) {
                    if (!ControllerTypeCache.Initialized) {
                        ControllerTypeCache.Initialize();
                        // Go through all assemblies referenced by the application and search for
                        // controllers and controller factories.
                        ICollection assemblies = BuildManager.GetReferencedAssemblies();
                        foreach (Assembly asm in assemblies) {
                            try {
                                Type[] typesInAsm = asm.GetTypes();
                                foreach (Type t in typesInAsm) {
                                    if (IsController(t)) {
                                        string foundControllerName = t.Name.Substring(0, t.Name.Length - 10);
                                        if (!ControllerTypeCache.ContainsController(foundControllerName)) {
                                            ControllerTypeCache.AddControllerType(foundControllerName, t);
                                        }
                                        else {
                                            // A null value indicates a conflict for this key (i.e. more than one match)
                                            ControllerTypeCache.AddControllerType(foundControllerName, null);
                                        }
                                    }
                                }
                            }
                            catch {
                                // TODO: Remove this try-catch. This is a temporary fix until we add a new feature.
                            }
                        }
                    }
                }
            } 
            // Once the master list of controllers has been created we can quickly index into it
            Type controllerType;
            if (ControllerTypeCache.TryGetControllerType(controllerName, out controllerType)) {
                if (controllerType == null) {
                    // A null value indicates a conflict for this key (i.e. more than one match)
                    throw new InvalidOperationException(
                        String.Format(
                            CultureInfo.CurrentUICulture,
                            MvcResources.DefaultControllerFactory_DuplicateControllers,
                            controllerName));
                }
                return controllerType;
            }
            return null;
        } 
        private static bool IsController(Type t) {
            return
                t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
                (t != typeof(IController)) &&
                (t != typeof(Controller)) &&
                typeof(IController).IsAssignableFrom(t);
        } 
        #region IControllerFactory Members
        IController IControllerFactory.CreateController(RequestContext context, string controllerName) {
            return CreateController(context, controllerName);
        } 
        void IControllerFactory.DisposeController(IController controller) {
            DisposeController(controller);
        }
        #endregion
    }
}
通过反射来创建 Controller 实例,GetControllerType 里面做了些缓存处理,以此来避免频繁使用反射造成的性能问题。继续 MvcHandler.ProcessRequest(),在得到控制器实例后,MvcHandler 开始了调用 Controller.Execute()来进一步后续操作,同时对其上下文进一步封装,除了前面创建的 RequestContext,还加上了当前这个 Controller对象的引用,类名叫ControllerContext。
namespace System.Web.Mvc {
    using System.Web.Routing; 
  [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
  [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
    public class ControllerContext : RequestContext {
        public ControllerContext(HttpContextBase httpContext, RouteData routeData, IController controller)
            : base(httpContext, routeData) {
            if (controller == null) {
                throw new ArgumentNullException("controller");
            }
            Controller = controller;
        } 
        public ControllerContext(RequestContext requestContext, IController controller)
            : this(GetRequestContext(requestContext).HttpContext, GetRequestContext(requestContext).RouteData, controller) {
        } 
        public IController Controller {
            get;
            private set;
        } 
        internal static RequestContext GetRequestContext(RequestContext requestContext) {
            if (requestContext == null) {
                throw new ArgumentNullException("requestContext");
            }
            return requestContext;
        }
    }
}
继续看IController的默认实现类Controller:
namespace System.Web.Mvc {
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Reflection;
    using System.Security.Principal;
    using System.Web;
    using System.Web.Mvc.Resources;
    using System.Web.Routing; 
  [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
  [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
    public class Controller : IController {
        private RouteCollection _routeCollection;
        private IDictionary<string, object> _viewData;
        private IViewEngine _viewEngine; 
        public ControllerContext ControllerContext {
            get;
            set;
        } 
        public HttpContextBase HttpContext {
            get {
                return ControllerContext == null ? null : ControllerContext.HttpContext;
            }
        } 
        public HttpRequestBase Request {
            get {
                return HttpContext == null ? null : HttpContext.Request;
            }
        } 
        public HttpResponseBase Response {
            get {
                return HttpContext == null ? null : HttpContext.Response;
            }
        } 
        internal RouteCollection RouteCollection {
            get {
                if (_routeCollection == null) {
                    _routeCollection = RouteTable.Routes;
                }
                return _routeCollection;
            }
            set {
                _routeCollection = value;
            }
        } 
        public RouteData RouteData {
            get {
                return ControllerContext == null ? null : ControllerContext.RouteData;
            }
        } 
        public HttpServerUtilityBase Server {
            get {
                return HttpContext == null ? null : HttpContext.Server;
            }
        } 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
            Justification = "This property is settable so that unit tests can provide mock implementations.")]
        public TempDataDictionary TempData {
            get;
            set;
        } 
        public IPrincipal User {
            get {
                return HttpContext == null ? null : HttpContext.User;
            }
        } 
        public IDictionary<string, object> ViewData {
            get {
                if (_viewData == null) {
                    _viewData = new Dictionary<string, object>();
                }
                return _viewData;
            }
        } 
        public IViewEngine ViewEngine {
            get {
                return _viewEngine ?? new WebFormViewEngine();
            }
            set {
                if (value == null) {
                    throw new ArgumentNullException("value");
                }
                _viewEngine = value;
            }
        } 
      private static object ConvertParameterType(object value, TypedestinationType, string parameterName, string actionName) {
            if (value == null || value.GetType() == destinationType) {
                return value;
            } 
            TypeConverter converter = TypeDescriptor.GetConverter(destinationType);
            bool canConvertFrom = converter.CanConvertFrom(value.GetType());
            if (!canConvertFrom) {
                converter = TypeDescriptor.GetConverter(value.GetType());
            }
            if (!(canConvertFrom || converter.CanConvertTo(destinationType))) {
                throw new InvalidOperationException(String.Format(
                    CultureInfo.CurrentUICulture,
                    MvcResources.Controller_CannotConvertParameter,
                    parameterName, actionName, value, destinationType));
            }
            try {
                return canConvertFrom ? converter.ConvertFrom(value) : converter.ConvertTo(value, destinationType);
            }
            catch (Exception ex) {
                throw new InvalidOperationException(String.Format(
                    CultureInfo.CurrentUICulture,
                    MvcResources.Controller_CannotConvertParameter,
                    parameterName, actionName, value, destinationType),
                    ex);
            }
        } 
        protected internal virtual void Execute(ControllerContext controllerContext) {
            if (controllerContext == null) {
                throw new ArgumentNullException("controllerContext");
            } 
            ControllerContext = controllerContext;
            TempData = new TempDataDictionary(controllerContext.HttpContext); 
            string actionName = RouteData.GetRequiredString("action");
            if (!InvokeAction(actionName)) {
                HandleUnknownAction(actionName);
            }
        } 
        protected internal virtual void HandleUnknownAction(string actionName) {
          throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_UnknownAction, actionName));
        } 
        protected internal bool InvokeAction(string actionName) {
            return InvokeAction(actionName, new RouteValueDictionary());
        } 
        protected internal virtual bool InvokeAction(string actionName, RouteValueDictionary values) {
            if (String.IsNullOrEmpty(actionName)) {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
            } 
            // We have to loop through all the methods to make sure there isn't
            // a conflict. If we stop the loop the first time we find a match
            // we might miss some error cases. 
            MemberInfo[] membInfos = GetType().GetMember(actionName, MemberTypes.Method,
                BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
            MethodInfo foundMatch = null; 
            foreach (MemberInfo memberInfo in membInfos) {
                MethodInfo mi = (MethodInfo)memberInfo; 
                // 1) Action methods must not have the non-action attribute in their inheritance chain, and
                // 2) special methods like constructors, property accessors, and event accessors cannot be action methods, and
                // 3) methods originally defined on Object (like ToString) or Controller cannot be action methods.
                if (!mi.IsDefined(typeof(NonActionAttribute), true) &&
                    !mi.IsSpecialName &&
                    mi.DeclaringType.IsSubclassOf(typeof(Controller))) {
                    if (foundMatch != null) {
                        throw new InvalidOperationException(
                          String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MoreThanOneAction, actionName, GetType()));
                    }
                    foundMatch = mi;
                }
            } 
            if (foundMatch != null) {
                InvokeActionMethod(foundMatch, values);
                return true;
            }
            return false;
        } 
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
            Justification = "We use MethodInfo since it represents only methods and not constructors." +
            "This method only makes sense for use with methods.")]
        protected internal virtual void InvokeActionMethod(MethodInfo methodInfo, RouteValueDictionary values) {
            if (methodInfo == null) {
                throw new ArgumentNullException("methodInfo");
            }
            if (values == null) {
                values = new RouteValueDictionary();
            }
            if (methodInfo.ContainsGenericParameters) {
              throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ActionCannotBeGeneric, methodInfo.Name));
            } 
            ParameterInfo[] methodParameters = methodInfo.GetParameters();
            object[] parameterValues = null;
            if (methodParameters.Length > 0) { 
                parameterValues = new object[methodParameters.Length];
                for (int i = 0; i < methodParameters.Length; i++) {
                    ParameterInfo pi = methodParameters
                    if (pi.IsOut || pi.ParameterType.IsByRef) {
                      throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ReferenceParametersNotSupported, pi.Name,methodInfo.Name));
                    } 
                    bool valueRequired = true;
                    if (pi.ParameterType.IsClass) {
                        // Classes (ref types) don't require values since we can pass in null
                        valueRequired = false;
                    }
                    else {
                        if ((pi.ParameterType.IsGenericType && !pi.ParameterType.IsGenericTypeDefinition) &&
                            (pi.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>))) {
                            // Nullable types don't require values since we can pass in null
                            valueRequired = false;
                        }
                    } 
                    // Try to get a value for the parameter. We use this order of precedence:
                    // 1. Explicitly-provided extra parameters in the call to InvokeAction()
                    // 2. Values from the RouteData (could be from the typed-in URL or from the route's default values)
                    // 3. Request values (query string, form post data, cookie)
                    object parameterValue = null;
                    if (!values.TryGetValue(methodParameters.Name, out parameterValue)) {
                        if (RouteData == null || !RouteData.Values.TryGetValue(methodParameters.Name, out parameterValue)) {
                            if (Request != null) {
                                parameterValue = Request[methodParameters.Name];
                            }
                        }
                    } 
                    if (parameterValue == null && valueRequired) {
                      throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name));
                    } 
                    try {
                      parameterValues = ConvertParameterType(parameterValue,methodParameters.ParameterType, methodParameters.Name,methodInfo.Name);
                    }
                    catch (Exception ex) {
                        // Parameter value conversion errors are acceptable unless the value is required
                        if (valueRequired) {
                          throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name),ex);
                        }
                    }
                }
            } 
            InvokeActionMethodFilters(methodInfo, () => methodInfo.Invoke(this, parameterValues));
        } 
        private void InvokeActionMethodFilters(MethodInfo methodInfo, Action continuation) { 
            // filters should execute in this order:
            // controller virtual overrides -> controller base attributes -> controller type attributes ->
            //  base action method attributes -> action method attributes 
            List<ActionFilterAttribute> filters = new List<ActionFilterAttribute>() {
                new ControllerActionFilter(this)
            }; 
            Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
            Type curType = GetType();
            while (curType != null) {
                memberChain.Push(curType);
                curType = curType.BaseType;
            } 
            List<ActionFilterAttribute> sortedClassFilters = SortActionFilters(memberChain);
            filters.AddRange(sortedClassFilters);
            List<ActionFilterAttribute> sortedMethodFilters = PrepareMethodActionFilters(methodInfo);
            filters.AddRange(sortedMethodFilters); 
            FilterContext context = new FilterContext(ControllerContext, methodInfo);
            ActionFilterExecutor executor = new ActionFilterExecutor(filters, context, continuation);
            executor.Execute();
        } 
        protected virtual void OnActionExecuted(FilterExecutedContext filterContext) {
        } 
        protected virtual void OnActionExecuting(FilterExecutingContext filterContext) {
        } 
        internal static List<ActionFilterAttribute> PrepareMethodActionFilters(MethodInfo methodInfo) { 
            Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
            memberChain.Push(methodInfo); 
            MethodInfo baseMethod = methodInfo.GetBaseDefinition();
            Type curType = methodInfo.DeclaringType.BaseType;
            while (true) {
                MemberInfo[] membInfos = curType.GetMember(methodInfo.Name, MemberTypes.Method,
                    BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
                MethodInfo foundMatch = null;
                foreach (MemberInfo memberInfo in membInfos) {
                    MethodInfo mi = (MethodInfo)memberInfo;
                    if (mi.GetBaseDefinition() == baseMethod && mi.DeclaringType == curType) {
                        foundMatch = mi;
                        break;
                    }
                }
                if (foundMatch == null) {
                    break;
                }
                memberChain.Push(foundMatch);
                curType = curType.BaseType;
            } 
            return SortActionFilters(memberChain);
        } 
        protected virtual void RedirectToAction(RouteValueDictionary values) {
            VirtualPathData vpd = RouteCollection.GetVirtualPath(ControllerContext, values);
            string target = null;
            if (vpd != null) {
                target = vpd.VirtualPath;
            }
            HttpContext.Response.Redirect(target);
        } 
        protected void RedirectToAction(string actionName) {
            if (String.IsNullOrEmpty(actionName)) {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
            }
            RouteValueDictionary valuesDictionary = new RouteValueDictionary();
            valuesDictionary.Add("action", actionName);
            RedirectToAction(valuesDictionary);
        } 
        protected void RedirectToAction(string actionName, string controllerName) {
            if (String.IsNullOrEmpty(actionName)) {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
            }
            if (String.IsNullOrEmpty(controllerName)) {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
            }
            RouteValueDictionary valuesDictionary = new RouteValueDictionary();
            valuesDictionary.Add("action", actionName);
            valuesDictionary.Add("controller", controllerName);
            RedirectToAction(valuesDictionary);
        } 
        protected void RenderView(string viewName) {
            RenderView(viewName, String.Empty, ViewData);
        } 
        protected void RenderView(string viewName, string masterName) {
            RenderView(viewName, masterName, ViewData);
        } 
        protected void RenderView(string viewName, object viewData) {
            RenderView(viewName, String.Empty, viewData);
        } 
        protected virtual void RenderView(string viewName, string masterName, object viewData) {
            ViewContext viewContext = new ViewContext(ControllerContext, viewName, masterName, viewData, TempData);
            ViewEngine.RenderView(viewContext);
        } 
        private static List<ActionFilterAttribute> SortActionFilters(Stack<MemberInfo> memberChain) {
            List<ActionFilterAttribute> filters = new List<ActionFilterAttribute>(); 
            foreach (MemberInfo member in memberChain) {
              ActionFilterAttribute[] attrs =(ActionFilterAttribute[])member.GetCustomAttributes(typeof(ActionFilterAttribute),false /* inherit */);
                SortedList<int,ActionFilterAttribute> orderedFilters = new SortedList<int,ActionFilterAttribute>();
                foreach (ActionFilterAttribute filter in attrs) {
                    // filters are allowed to have the same order only if the order is -1.  in that case,
                    // they are processed before explicitly ordered filters but in no particular order in
                    // relation to one another.
                    if (filter.Order >= 0) {
                        if (orderedFilters.ContainsKey(filter.Order)) {
                            throw new InvalidOperationException(
                                String.Format(
                                    CultureInfo.CurrentUICulture,
                                    MvcResources.ActionFilter_DuplicateOrder,
                                    member,
                                    filter.Order));
                        }
                        orderedFilters.Add(filter.Order, filter);
                    }
                    else {
                        filters.Add(filter);
                    }
                }
                filters.AddRange(orderedFilters.Values);
            } 
            return filters;
        } 
        #region IController Members
        void IController.Execute(ControllerContext controllerContext) {
            Execute(controllerContext);
        }
        #endregion 
        private sealed class ControllerActionFilter : ActionFilterAttribute { 
            private Controller _controller; 
            public ControllerActionFilter(Controller controller) {
                _controller = controller;
            } 
            public override void OnActionExecuted(FilterExecutedContext filterContext) {
                _controller.OnActionExecuted(filterContext);
            } 
            public override void OnActionExecuting(FilterExecutingContext filterContext) {
                _controller.OnActionExecuting(filterContext);
            } 
        } 
    }
}
获取 Action 的名字,然后开始执行 InvokeAction,如果找不到Action,则调用HandleUnknownAction,这是一个虚拟方法,可以在子类中重写,默认是抛出一个异常InvalidOperationException。
          protected internal virtual void Execute(ControllerContext controllerContext) {
            if (controllerContext == null) {
                throw new ArgumentNullException("controllerContext");
            } 
            ControllerContext = controllerContext;
            TempData = new TempDataDictionary(controllerContext.HttpContext); 
            string actionName = RouteData.GetRequiredString("action");
            if (!InvokeAction(actionName)) {
                HandleUnknownAction(actionName);
            }
        }
详细看看InvokeAction方式的执行:
protected internal bool InvokeAction(string actionName) {
            return InvokeAction(actionName, new RouteValueDictionary());
        } 
        protected internal virtual bool InvokeAction(string actionName, RouteValueDictionary values) {
            if (String.IsNullOrEmpty(actionName)) {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
            } 
            // We have to loop through all the methods to make sure there isn't
            // a conflict. If we stop the loop the first time we find a match
            // we might miss some error cases. 
            MemberInfo[] membInfos = GetType().GetMember(actionName, MemberTypes.Method,
                BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
            MethodInfo foundMatch = null; 
            foreach (MemberInfo memberInfo in membInfos) {
                MethodInfo mi = (MethodInfo)memberInfo; 
                // 1) Action methods must not have the non-action attribute in their inheritance chain, and
                // 2) special methods like constructors, property accessors, and event accessors cannot be action methods, and
                // 3) methods originally defined on Object (like ToString) or Controller cannot be action methods.
                if (!mi.IsDefined(typeof(NonActionAttribute), true) &&
                    !mi.IsSpecialName &&
                    mi.DeclaringType.IsSubclassOf(typeof(Controller))) {
                    if (foundMatch != null) {
                        throw new InvalidOperationException(
                          String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MoreThanOneAction, actionName, GetType()));
                    }
                    foundMatch = mi;
                }
            } 
            if (foundMatch != null) {
                InvokeActionMethod(foundMatch, values);
                return true;
            }
            return false;
        }
它通过反射获取所有同名 Action 方法信息;其次,它过滤掉所有有 NonActionAttribute 和 IsSpecialName标记的方法;第三,当同名有效 Action被重载时它会抛出异常(提示Controller_MoreThanOneAction),继续调用InvokeActionMethod:
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
    Justification = "We use MethodInfo since it represents only methods and not constructors." +
    "This method only makes sense for use with methods.")]
protected internal virtual void InvokeActionMethod(MethodInfo methodInfo, RouteValueDictionary values) {
    if (methodInfo == null) {
        throw new ArgumentNullException("methodInfo");
    }
    if (values == null) {
        values = new RouteValueDictionary();
    }
    if (methodInfo.ContainsGenericParameters) {
        throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ActionCannotBeGeneric, methodInfo.Name));
    } 
    ParameterInfo[] methodParameters = methodInfo.GetParameters();
    object[] parameterValues = null;
    if (methodParameters.Length > 0) { 
        parameterValues = new object[methodParameters.Length];
        for (int i = 0; i < methodParameters.Length; i++) {
            ParameterInfo pi = methodParameters
            if (pi.IsOut || pi.ParameterType.IsByRef) {
                throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ReferenceParametersNotSupported, pi.Name,methodInfo.Name));
            } 
            bool valueRequired = true;
            if (pi.ParameterType.IsClass) {
                // Classes (ref types) don't require values since we can pass in null
                valueRequired = false;
            }
            else {
                if ((pi.ParameterType.IsGenericType && !pi.ParameterType.IsGenericTypeDefinition) &&
                    (pi.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>))) {
                    // Nullable types don't require values since we can pass in null
                    valueRequired = false;
                }
            } 
            // Try to get a value for the parameter. We use this order of precedence:
            // 1. Explicitly-provided extra parameters in the call to InvokeAction()
            // 2. Values from the RouteData (could be from the typed-in URL or from the route's default values)
            // 3. Request values (query string, form post data, cookie)
            object parameterValue = null;
            if (!values.TryGetValue(methodParameters.Name, out parameterValue)) {
                if (RouteData == null || !RouteData.Values.TryGetValue(methodParameters.Name, out parameterValue)) {
                    if (Request != null) {
                        parameterValue = Request[methodParameters.Name];
                    }
                }
            } 
            if (parameterValue == null && valueRequired) {
                throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name));
            } 
            try {
                parameterValues = ConvertParameterType(parameterValue,methodParameters.ParameterType, methodParameters.Name,methodInfo.Name);
            }
            catch (Exception ex) {
                // Parameter value conversion errors are acceptable unless the value is required
                if (valueRequired) {
                    throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name),ex);
                }
            }
        }
    } 
    InvokeActionMethodFilters(methodInfo, () => methodInfo.Invoke(this, parameterValues));
}
这个方法很复杂,有大量的代码是参数的分解 ,只有最后一行是关键的。 InvokeActionMethodFilters(methodInfo, () => methodInfo.Invoke(this, parameterValues)); 
这行代码将 Action 的调用作为一个委托,连同反射信息传递给 InvokeActionMethodFilters。
private void InvokeActionMethodFilters(MethodInfo methodInfo, Action continuation) { 
          // filters should execute in this order:
          // controller virtual overrides -> controller base attributes -> controller type attributes ->
          //  base action method attributes -> action method attributes 
          List<ActionFilterAttribute> filters = new List<ActionFilterAttribute>() {
              new ControllerActionFilter(this)
          }; 
          Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
          Type curType = GetType();
          while (curType != null) {
              memberChain.Push(curType);
              curType = curType.BaseType;
          } 
          List<ActionFilterAttribute> sortedClassFilters = SortActionFilters(memberChain);
          filters.AddRange(sortedClassFilters);
          List<ActionFilterAttribute> sortedMethodFilters = PrepareMethodActionFilters(methodInfo);
          filters.AddRange(sortedMethodFilters); 
          FilterContext context = new FilterContext(ControllerContext, methodInfo);
          ActionFilterExecutor executor = new ActionFilterExecutor(filters, context, continuation);
          executor.Execute();
      } 
这个方法首先将默认的过滤器 ControllerActionFilter加到列表,然后提取所有继承层次上基类的过滤器特性。最后将这些过滤器集合、过滤上下文,连同前一个方法传递进来的 Action执行委托(continuation) 再次转交给了一个 ActionFilterExecutor 对象实例,并调用其 Execute 方法。
namespace System.Web.Mvc {
    using System;
    using System.Collections;
    using System.Collections.Generic; 
    internal sealed class ActionFilterExecutor { 
        private FilterContext _context;
        private Action _continuation;
        private List<ActionFilterAttribute> _filters; 
        public ActionFilterExecutor(List<ActionFilterAttribute> filters, FilterContext context, Action continuation) {
            _filters = filters;
            _context = context;
            _continuation = continuation;
        } 
        public void Execute() {
            IEnumerator<ActionFilterAttribute> enumerator = _filters.GetEnumerator();
            ExecuteRecursive(enumerator);
        } 
        private FilterExecutedContext ExecuteRecursive(IEnumerator<ActionFilterAttribute> enumerator) {
            if (enumerator.MoveNext()) {
                ActionFilterAttribute filter = enumerator.Current;
                FilterExecutingContext preContext = new FilterExecutingContext(_context);
                filter.OnActionExecuting(preContext);
                if (preContext.Cancel) {
                    return new FilterExecutedContext(_context, null /* exception */);
                } 
                bool wasError = false;
                FilterExecutedContext postContext = null;
                try {
                    postContext = ExecuteRecursive(enumerator);
                }
                catch (Exception ex) {
                    wasError = true;
                    postContext = new FilterExecutedContext(_context, ex);
                    filter.OnActionExecuted(postContext);
                    if (!postContext.ExceptionHandled) {
                        throw;
                    }
                }
                if (!wasError) {
                    filter.OnActionExecuted(postContext);
                }
                return postContext;
            }
            else {
                _continuation();
                return new FilterExecutedContext(_context, null /* exception */);
            }
        } 
    }
}
ExecuteRecursive使用了递归算法,通过迭代器 MoveNext() 方法提取一个过滤器对象,执行其 OnActionExecuting 方法。 如果该方法设置了filterContext.Cancel =true,则放弃后续执行代码。这种机制为我们提供了更好的控制,例如用它来实现伪静态页。一层一层调用所有的ActionFilterAttribute.OnActionExecuting 方法,直到 MoveNext() == false。在最后一次递归调用时,由于 enumerator.MoveNext() == false, _continuation()方法被执行。这个就是前面给传递过来的 Action 方法委托,Action 方法总算是执行了。 在 Action委托执行完成后,递归调逐级往上回溯,直到最初那个方法堆栈。这样所有ActionFilterAttribute.OnActionExecuted也被执行完成。
到此开始进入最后的视图呈现阶段,可以把数据呈现到视图上,Controller 提供了几个重载的 RenderView() 来完成这个工作。
      protected void RenderView(string viewName) {
          RenderView(viewName, String.Empty, ViewData);
      } 
      protected void RenderView(string viewName, string masterName) {
          RenderView(viewName, masterName, ViewData);
      } 
      protected void RenderView(string viewName, object viewData) {
          RenderView(viewName, String.Empty, viewData);
      } 
      protected virtual void RenderView(string viewName, string masterName, object viewData) {
          ViewContext viewContext = new ViewContext(ControllerContext, viewName, masterName, viewData, TempData);
          ViewEngine.RenderView(viewContext);
      }
将一路传递过来的相关 "数据" (上下文)ControllerContext 再次包装成ViewContext。当然,这次依然会多出些东西,里面就有我们向视图传递的数据 —— viewData 和tempData。作为默认选择,MVC 创建WebForm 视图引擎来展示结果。其他的视图引擎可以去看mvccontrib,这个项目就是整合:Castle WindsorStructureMapSpring.NET 等IoC框架以及视图引擎,包括Castle MonoRail所用的NVelocityView视图引擎、NHamlView视图引擎、XsltViewEngine视图引擎等等。
继续看这个 WebFormViewEngine:
namespace System.Web.Mvc {
    using System;
    using System.Globalization;
    using System.Web.Compilation;
    using System.Web.Resources;
    using System.Web.Routing;
    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 WebFormViewEngine : IViewEngine {
        private IBuildManager _buildManager;
        private IViewLocator _viewLocator; 
        internal IBuildManager BuildManager {
            get {
                if (_buildManager == null) {
                    _buildManager = new BuildManagerWrapper();
                }
                return _buildManager;
            }
            set {
                _buildManager = value;
            }
        } 
        public IViewLocator ViewLocator {
            get {
                if (_viewLocator == null) {
                    _viewLocator = new WebFormViewLocator();
                }
                return _viewLocator;
            }
            set {
                _viewLocator = value;
            }
        } 
        protected virtual void RenderView(ViewContext viewContext) {
            if (viewContext == null) {
                throw new ArgumentNullException("viewContext");
            } 
            string viewPath = ViewLocator.GetViewLocation(viewContext, viewContext.ViewName);
            if (String.IsNullOrEmpty(viewPath)) {
                throw new InvalidOperationException(
                    String.Format(
                        CultureInfo.CurrentUICulture,
                        MvcResources.WebFormViewEngine_ViewNotFound,
                        viewContext.ViewName));
            }
            object viewInstance = BuildManager.CreateInstanceFromVirtualPath(viewPath, typeof(object));
            if (viewInstance == null) {
                throw new InvalidOperationException(
                    String.Format(
                        CultureInfo.CurrentUICulture,
                        MvcResources.WebFormViewEngine_ViewCouldNotBeCreated,
                        viewPath));
            } 
            ViewPage viewPage = viewInstance as ViewPage; 
            if (viewPage != null) { 
                if (!String.IsNullOrEmpty(viewContext.MasterName)) {
                    string masterLocation = ViewLocator.GetMasterLocation(viewContext, viewContext.MasterName);
                    if (String.IsNullOrEmpty(masterLocation)) {
                        throw new InvalidOperationException(
                            String.Format(
                                CultureInfo.CurrentUICulture,
                                MvcResources.WebFormViewEngine_MasterNotFound,
                                viewContext.MasterName));
                    } 
                    // We don't set the page's MasterPageFile directly since it will get
                    // overwritten by a statically-defined value. In ViewPage we wait until
                    // the PreInit phase until we set the new value.
                    viewPage.MasterLocation = masterLocation;
                }
                viewPage.SetViewData(viewContext.ViewData);
                viewPage.RenderView(viewContext);
            }
            else {
                ViewUserControl viewUserControl = viewInstance as ViewUserControl;
                if (viewUserControl != null) {
                    if (!String.IsNullOrEmpty(viewContext.MasterName)) {
                        throw new InvalidOperationException(MvcResources.WebFormViewEngine_UserControlCannotHaveMaster);
                    } 
                    viewUserControl.SetViewData(viewContext.ViewData);
                    viewUserControl.RenderView(viewContext);
                }
                else {
                    throw new InvalidOperationException(
                        String.Format(
                            CultureInfo.CurrentUICulture,
                            MvcResources.WebFormViewEngine_WrongViewBase,
                            viewPath));
                }
            }
        } 
        #region IViewEngine Members
        void IViewEngine.RenderView(ViewContext viewContext) {
            RenderView(viewContext);
        }
        #endregion
    }
}
首先会创建一个 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.BuildManagerBuildManager 类管理应用程序的程序集和页的编译过程),后面通过 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;
            }
        }&n