首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >ASP.NET MVC Preview生命周期分析

ASP.NET MVC Preview生命周期分析

作者头像
张善友
发布于 2018-01-30 09:57:19
发布于 2018-01-30 09:57:19
1.6K00
代码可运行
举报
文章被收录于专栏:张善友的专栏张善友的专栏
运行总次数:0
代码可运行

做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)开始他的执行流程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<httpModules>

     <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing" />

</httpModules>

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 new InvalidOperationException(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 new InvalidOperationException(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 管线会依次处理下面的请求:

  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 属性:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 集合:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 { controller = "User", action = "Login" }), new MvcRouteHandler()));

           routes.Add(new Route("User/Logout", new RouteValueDictionary(new { controller = "User", action = "Logout" }), new MvcRouteHandler()));

           routes.Add(new Route("User/Signup", new RouteValueDictionary(new { controller = "User", action = "Signup" }), new MvcRouteHandler()));

           routes.Add(new Route("User/SendPassword", new RouteValueDictionary(new { controller = "User", action = "SendPassword" }), new MvcRouteHandler())); 



           routes.Add(new Route("Story/Detail/{id}", new RouteValueDictionary(new { controller = "Story", action = "Detail" }), new MvcRouteHandler()));

           routes.Add(new Route("Story/Upcoming/{page}", new RouteValueDictionary(new { controller = "Story", action = "Upcoming" }), new MvcRouteHandler()));

           routes.Add(new Route("Story/Search/{q}/{page}", new RouteValueDictionary(new { controller = "Story", action = "Search" }), new MvcRouteHandler())); 



           var defaults = new RouteValueDictionary (

                                                       new

                                                       {

                                                           controller = "Story",

                                                           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 { controller = "User", action = "Login" }), new MvcRouteHandler()));

           routes.Add(new Route("User.mvc/Logout", new RouteValueDictionary(new { controller = "User", action = "Logout" }), new MvcRouteHandler()));

           routes.Add(new Route("User.mvc/Signup", new RouteValueDictionary(new { controller = "User", action = "Signup" }), new MvcRouteHandler()));

           routes.Add(new Route("User.mvc/SendPassword", new RouteValueDictionary(new { controller = "User", action = "SendPassword" }), new MvcRouteHandler())); 



           routes.Add(new Route("Story.mvc/Detail/{id}", new RouteValueDictionary(new { controller = "Story", action = "Detail" }), new MvcRouteHandler()));

           routes.Add(new Route("Story.mvc/Upcoming/{page}", new RouteValueDictionary(new { controller = "Story", action = "Upcoming" }), new MvcRouteHandler()));

           routes.Add(new Route("Story.mvc/Search/{q}/{page}", new RouteValueDictionary(new { controller = "Story", action = "Search" }), new MvcRouteHandler())); 



           var defaults = new RouteValueDictionary(

                                                       new

                                                       {

                                                           controller = "Story",

                                                           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:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)的执行流程了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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.NET MVC的流程。现在来继续看MvcHandler。

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
internal ControllerBuilder ControllerBuilder {

            get {

                if (_controllerBuilder == null) {

                    _controllerBuilder = ControllerBuilder.Current;

                }

                return _controllerBuilder;

            }

            set {

                _controllerBuilder = value;

            }

        }

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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的代码:

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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, Type destinationType, 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 new InvalidOperationException(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 new InvalidOperationException(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[i]; 



                    if (pi.IsOut || pi.ParameterType.IsByRef) {

                        throw new InvalidOperationException(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[i].Name, out parameterValue)) {

                        if (RouteData == null || !RouteData.Values.TryGetValue(methodParameters[i].Name, out parameterValue)) {

                            if (Request != null) {

                                parameterValue = Request[methodParameters[i].Name];

                            }

                        }

                    } 



                    if (parameterValue == null && valueRequired) {

                        throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name));

                    } 



                    try {

                        parameterValues[i] = ConvertParameterType(parameterValue, methodParameters[i].ParameterType, methodParameters[i].Name, methodInfo.Name);

                    }

                    catch (Exception ex) {

                        // Parameter value conversion errors are acceptable unless the value is required

                        if (valueRequired) {

                            throw new InvalidOperationException(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。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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方式的执行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[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 new InvalidOperationException(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[i]; 



             if (pi.IsOut || pi.ParameterType.IsByRef) {

                 throw new InvalidOperationException(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[i].Name, out parameterValue)) {

                 if (RouteData == null || !RouteData.Values.TryGetValue(methodParameters[i].Name, out parameterValue)) {

                     if (Request != null) {

                         parameterValue = Request[methodParameters[i].Name];

                     }

                 }

             } 



             if (parameterValue == null && valueRequired) {

                 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name));

             } 



             try {

                 parameterValues[i] = ConvertParameterType(parameterValue, methodParameters[i].ParameterType, methodParameters[i].Name, methodInfo.Name);

             }

             catch (Exception ex) {

                 // Parameter value conversion errors are acceptable unless the value is required

                 if (valueRequired) {

                     throw new InvalidOperationException(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。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 方法。

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 对象来获取视图存放路径。

代码语言:js
AI代码解释
复制
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的代码:

代码语言:js
AI代码解释
复制
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。

代码语言:js
AI代码解释
复制
    private sealed class ViewUserControlContainerPage : ViewPage {
            public ViewUserControlContainerPage(ViewUserControl userControl) {
                Controls.Add(userControl);
            }
        } 

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2008-03-22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
小白必定收藏的机器学习文章
本篇文章源自武博士,这篇文章在gitchat上原本收费,现在公开分享。 一. 引言
公众号guangcity
2019/09/20
5320
小白必定收藏的机器学习文章
一文带你了解机器学习的四大框架PyTorch、TensorFlow、Keras、Scikit-learn
点评:文章概要性地介绍了机器学习,指出其重要性日益凸显,如Geoffrey Hinton等机器学习领域的大牛被Google、Facebook等科技巨头争相聘请。文章阐述了机器学习的定义,即一种让计算机从数据中自动学习和改进的技术。同时,文章还探讨了机器学习的范围、方法,如支持向量机、聚类算法等,并强调了机器学习在大数据、深度学习及人工智能领域的广泛应用与重要影响,展现了机器学习技术推动科技进步的巨大潜力。
小白的大数据之旅
2024/11/25
1.7K1
一文带你了解机器学习的四大框架PyTorch、TensorFlow、Keras、Scikit-learn
机器学习 | 四大常用机器学习Python库介绍
今天这篇我们介绍下Python中常用的机器学习库(机器学习、深度学习啥的,小编还是建议使用Python进行建模编写哈),也算是本公号机器学习的第一篇推文,主要内容如下:
DataCharm
2021/03/11
5.1K0
机器学习 | 四大常用机器学习Python库介绍
用Python实现机器学习算法——简单的神经网络
导读:Python 被称为是最接近 AI 的语言。最近一位名叫Anna-Lena Popkes的小姐姐在GitHub上分享了自己如何使用Python(3.6及以上版本)实现7种机器学习算法的笔记,并附有完整代码。所有这些算法的实现都没有使用其他机器学习库。这份笔记可以帮大家对算法以及其底层结构有个基本的了解,但并不是提供最有效的实现。
IT派
2018/07/30
7340
用Python实现机器学习算法——简单的神经网络
【机器学习】探索未来科技的前沿:人工智能、机器学习与大模型
随着科技的不断进步,人工智能(AI)、机器学习(ML)和大模型(Large Models)成为了现代计算机科学领域的核心技术。它们不仅推动了科学研究的进步,也在多个行业中掀起了革命性的变革。从自动驾驶汽车到智能语音助手,再到精准医疗和金融预测,这些技术的应用已经深入到我们日常生活的方方面面。本文将深入探讨这三大技术的基本概念、历史发展、实现原理及其在实际生活中的应用,旨在为读者提供一个全面而深入的了解。
E绵绵
2024/06/01
9950
【算法】Python实现机器学习算法
小编邀请您,先思考: 1 您使用Python做过什么? 2 如何用Python实现决策树系列算法? 人生苦短,就用 Python。 在 Kaggle 最新发布的全球数据科学/机器学习现状报告中,来自
陆勤_数据人网
2018/04/18
1.9K0
【算法】Python实现机器学习算法
关于深度学习系列笔记(一)
第一个深度学习笔记吧,看书有一阵子了,对理论知识仍然稀里糊涂的,不过一边实操一边记笔记一边查资料,希望逐步再深入到理论里去,凡事开头难,也不怕他人笑话。一般深度学习都是从手写数字识别开始的。
python与大数据分析
2022/03/11
4510
关于深度学习系列笔记(一)
十大机器学习库,超全面总结!!
也毫无疑问,Pytorch的同学以压倒性的话语权霸屏。其实无论使用哪种框架,适合自己适合项目是最合适的。
Python编程爱好者
2024/05/22
8750
十大机器学习库,超全面总结!!
笔记|李宏毅老师机器学习课程,视频15Keras Demo
感谢李宏毅老师的分享,他的课程帮助我更好地学习、理解和应用机器学习。李老师的网站:http://speech.ee.ntu.edu.tw/~tlkagk/index.html。这个学习笔记是根据李老师2017年秋季机器学习课程的视频和讲义做的记录和总结。
陆勤_数据人网
2021/04/22
4260
完全汇总,十大机器学习算法!!
接下来我会从每个算法模型的介绍、基本原理、优缺点以及适用场景注意叙述,最后会基于开源数据集给出一个比较入门型的案例供大家学习~
Python编程爱好者
2024/04/12
5720
完全汇总,十大机器学习算法!!
教程丨机器学习算法:从头开始构建逻辑回归模型
原作:Rohith Gandhi 郭一璞 编译自 Hacher Noon 量子位 出品 | 公众号 QbitAI 逻辑回归是继线性回归之后最著名的机器学习算法。 在很多方面,线性回归和逻辑回归是相似的
量子位
2018/07/20
6540
一文讲透机器学习超参数调优(附代码)
超参数是在模型训练之外设置的选项,不会在训练过程中被优化或更改。相反,需要在训练之前手动设置它们,并且对模型的性能有很大的影响。
皮大大
2023/09/07
1.7K2
用Keras解决机器学习问题!
本文介绍3个案例来帮助读者认识和入门深度学习框架Keras。3个案例解决3个问题:回归、二分类、多分类.
Datawhale
2022/05/29
7220
用Keras解决机器学习问题!
深度学习算法中的非线性独立成分分析(Nonlinear Independent Component Analysis in Deep Learning)
深度学习是一种强大的机器学习技术,已经在计算机视觉、自然语言处理、语音识别等领域取得了巨大成功。然而,在深度学习中,由于网络层数的增加和复杂的非线性变换,传统的线性独立成分分析(Linear Independent Component Analysis,简称LICA)的有效性受到了限制。为了解决这个问题,研究人员提出了一种新的方法,即深度学习算法中的非线性独立成分分析(Nonlinear Independent Component Analysis,简称NLICA)。
大盘鸡拌面
2023/09/22
4660
深度学习入门(一),从Keras开始
安装环境:Anaconda(python3.6) 首先安装:tensorflow.(通过navigator,可参见直通车) 检验:import tensorflow as tf Keras在anaconda下没有原装的安装包,只有使用pip安装方式,安装Keras,pip install Keras
学到老
2019/01/25
2.3K0
深度学习入门(一),从Keras开始
[Python人工智能] 十九.Keras搭建循环神经网络分类案例及RNN原理详解
从本专栏开始,作者正式研究Python深度学习、神经网络及人工智能相关知识。前一篇文章分享了卷积神经网络CNN原理,并通过Keras编写CNN实现了MNIST分类学习案例。这篇文章将详细讲解循环神经网络RNN的原理知识,并采用Keras实现手写数字识别的RNN分类案例及可视化呈现。基础性文章,希望对您有所帮助!
Eastmount
2022/11/25
1.2K0
机器学习算法Python实现--逻辑回归
传送门:https://github.com/lawlite19/MachineLearning_Python/tree/master/LogisticRegression
Ai学习的老章
2019/04/10
7820
机器学习算法Python实现--逻辑回归
机器学习算法整理(三)逻辑回归
逻辑回归是解决分类问题的,那回归问题怎么解决分类问题呢?将样本的特征和样本发生的概率联系起来,概率是一个数。
算法之名
2021/09/14
3950
机器学习算法整理(三)逻辑回归
【人工智能】全景解析:【机器学习】【深度学习】从基础理论到应用前景的【深度探索】
无监督学习 是一种机器学习类型,模型在没有标注数据的情况下,通过识别数据中的模式和结构进行学习。
小李很执着
2024/08/14
3210
小姐姐带你一起学:如何用Python实现7种机器学习算法(附代码)
2018 区块链技术及应用峰会(BTA)·中国 倒计时 1 天 2018,想要follow最火的区块链技术?你还差一场严谨纯粹的技术交流会——2018区块链技术及应用峰会(BTA)·中国将于2018年3月30-31日登陆北京喜来登长城饭店。追求专业性?你要的这里全都有:当超强嘉宾阵容遇上业界同好的脑洞大联欢,1+1=无限可能,目前门票预购火热进行中。 活动详情: http://dwz.cn/7FI1Ch 编译 | 林椿眄 出品 | 人工智能头条(公众号ID:AI_Thinker) 【AI科技大本营导读】P
用户1737318
2018/06/05
1.8K0
推荐阅读
相关推荐
小白必定收藏的机器学习文章
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档