zoukankan      html  css  js  c++  java
  • Asp.Net MVC中Controller、Action、View是如何激活调用的

      上篇我们介绍了MVC的路由,知道在注册路由的时候会创建一个MvcHandler将其和Url规则一起放入到了RouteCollection中,之后请求通过UrlRoutingModule,根据当前的URL去RouteCollection中找到MVCRouteHandler,而MVCRouteHandler的GetHttpHandler方法返回了一个MvcHandler对象。

      接下来我们来看下MvcHandler源码做了什么,注意下面标注红色的代码,在ProquestRequestInit里面通过this.ControllerBuilder.GetControllerFactory();继续跟进ControllerBuilder源码,知道其创建的是DefaultControllerFactory,然后再通过反射创建了Controller实例。获取到Controller实例后,其再通过 controller.Execute(this.RequestContext);去RouteData中查找Action,通过反射调用Action,此时会检测所有的特性,首先检测的是AuthenticationFilters,验证通过后再执行其他的filter动作和Action 的行为。

      整个的过程大概是:获取到Controller实例后 --> CreateActionInvoker --> ActionInvoker --> ControllerActionInvoker.InvokeAction -->调用ExecuteResult -->找View --> 调用IView.Render --> 将信息写入到Response.Out。

    /// <summary>Selects the controller that will handle an HTTP request.</summary>
        public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
        {
            private struct ProcessRequestState
            {
                internal IAsyncController AsyncController;
                internal IControllerFactory Factory;
                internal RequestContext RequestContext;
                internal void ReleaseController()
                {
                    this.Factory.ReleaseController(this.AsyncController);
                }
            }
            private static readonly object _processRequestTag = new object();
            internal static readonly string MvcVersion = MvcHandler.GetMvcVersionString();
            /// <summary>Contains the header name of the ASP.NET MVC version.</summary>
            public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version";
            private ControllerBuilder _controllerBuilder;
            internal ControllerBuilder ControllerBuilder
            {
                get
                {
                    if (this._controllerBuilder == null)
                    {
                        this._controllerBuilder = ControllerBuilder.Current;
                    }
                    return this._controllerBuilder;
                }
                set
                {
                    this._controllerBuilder = value;
                }
            }
            /// <summary>Gets or sets a value that indicates whether the MVC response header is disabled.</summary>
            /// <returns>true if the MVC response header is disabled; otherwise, false.</returns>
            public static bool DisableMvcResponseHeader
            {
                get;
                set;
            }
            /// <summary>Gets a value that indicates whether another request can use the <see cref="T:System.Web.IHttpHandler" /> instance.</summary>
            /// <returns>true if the <see cref="T:System.Web.IHttpHandler" /> instance is reusable; otherwise, false.</returns>
            protected virtual bool IsReusable
            {
                get
                {
                    return false;
                }
            }
            /// <summary>Gets the request context.</summary>
            /// <returns>The request context.</returns>
            public RequestContext RequestContext
            {
                get;
                private set;
            }
            bool IHttpHandler.IsReusable
            {
                get
                {
                    return this.IsReusable;
                }
            }
            /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcHandler" /> class.</summary>
            /// <param name="requestContext">The request context.</param>
            /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception>
            public MvcHandler(RequestContext requestContext)
            {
                if (requestContext == null)
                {
                    throw new ArgumentNullException("requestContext");
                }
                this.RequestContext = requestContext;
            }
            /// <summary>Adds the version header by using the specified HTTP context.</summary>
            /// <param name="httpContext">The HTTP context.</param>
            protected internal virtual void AddVersionHeader(HttpContextBase httpContext)
            {
                if (!MvcHandler.DisableMvcResponseHeader)
                {
                    httpContext.Response.AppendHeader(MvcHandler.MvcVersionHeaderName, MvcHandler.MvcVersion);
                }
            }
            /// <summary>Called by ASP.NET to begin asynchronous request processing.</summary>
            /// <returns>The status of the asynchronous call.</returns>
            /// <param name="httpContext">The HTTP context.</param>
            /// <param name="callback">The asynchronous callback method.</param>
            /// <param name="state">The state of the asynchronous object.</param>
            protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)
            {
                HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
                return this.BeginProcessRequest(httpContext2, callback, state);
            }
            /// <summary>Called by ASP.NET to begin asynchronous request processing using the base HTTP context.</summary>
            /// <returns>The status of the asynchronous call.</returns>
            /// <param name="httpContext">The HTTP context.</param>
            /// <param name="callback">The asynchronous callback method.</param>
            /// <param name="state">The state of the asynchronous object.</param>
            protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
            {
                IController controller;
                IControllerFactory factory;
                this.ProcessRequestInit(httpContext, out controller, out factory);
                IAsyncController asyncController = controller as IAsyncController;
                if (asyncController != null)
                {
                    BeginInvokeDelegate<MvcHandler.ProcessRequestState> beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState)
                    {
                        IAsyncResult result;
                        try
                        {
                            result = innerState.AsyncController.BeginExecute(innerState.RequestContext, asyncCallback, asyncState);
                        }
                        catch
                        {
                            innerState.ReleaseController();
                            throw;
                        }
                        return result;
                    }
                    ;
                    EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> endDelegate = delegate(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState)
                    {
                        try
                        {
                            innerState.AsyncController.EndExecute(asyncResult);
                        }
                        finally
                        {
                            innerState.ReleaseController();
                        }
                    }
                    ;
                    MvcHandler.ProcessRequestState invokeState = new MvcHandler.ProcessRequestState
                    {
                        AsyncController = asyncController, 
                        Factory = factory, 
                        RequestContext = this.RequestContext
                    };
                    SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext();
                    return AsyncResultWrapper.Begin<MvcHandler.ProcessRequestState>(callback, state, beginDelegate, endDelegate, invokeState, MvcHandler._processRequestTag, -1, synchronizationContext);
                }
                Action action = delegate
                {
                    try
                    {
                        controller.Execute(this.RequestContext);
                    }
                    finally
                    {
                        factory.ReleaseController(controller);
                    }
                }
                ;
                return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag);
            }
            /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary>
            /// <param name="asyncResult">The asynchronous result.</param>
            protected internal virtual void EndProcessRequest(IAsyncResult asyncResult)
            {
                AsyncResultWrapper.End(asyncResult, MvcHandler._processRequestTag);
            }
            private static string GetMvcVersionString()
            {
                return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);
            }
            /// <summary>Processes the request by using the specified HTTP request context.</summary>
            /// <param name="httpContext">The HTTP context.</param>
            protected virtual void ProcessRequest(HttpContext httpContext)
            {
                HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
                this.ProcessRequest(httpContext2);
            }
            /// <summary>Processes the request by using the specified base HTTP request context.</summary>
            /// <param name="httpContext">The HTTP context.</param>
            protected internal virtual void ProcessRequest(HttpContextBase httpContext)
            {
                IController controller;
                IControllerFactory controllerFactory;
                this.ProcessRequestInit(httpContext, out controller, out controllerFactory);
                try
                {
                    controller.Execute(this.RequestContext);
                }
                finally
                {
                    controllerFactory.ReleaseController(controller);
                }
            }
            private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
            {
                HttpContext current = HttpContext.Current;
                if (current != null)
                {
                    bool? flag = ValidationUtility.IsValidationEnabled(current);
                    bool? flag2 = flag;
                    if (flag2.GetValueOrDefault() && flag2.HasValue)
                    {
                        ValidationUtility.EnableDynamicValidation(current);
                    }
                }
                this.AddVersionHeader(httpContext);
                this.RemoveOptionalRoutingParameters();
                string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
                factory = this.ControllerBuilder.GetControllerFactory();
                controller = factory.CreateController(this.RequestContext, requiredString);
                if (controller == null)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]
                    {
                        factory.GetType(), 
                        requiredString
                    }));
                }
            }
            private void RemoveOptionalRoutingParameters()
            {
                RouteValueDictionary values = this.RequestContext.RouteData.Values;
                values.RemoveFromDictionary((KeyValuePair<string, object> entry) => entry.Value == UrlParameter.Optional);
            }
            void IHttpHandler.ProcessRequest(HttpContext httpContext)
            {
                this.ProcessRequest(httpContext);
            }
            IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
            {
                return this.BeginProcessRequest(context, cb, extraData);
            }
            void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
            {
                this.EndProcessRequest(result);
            }
        }
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Web.Mvc.Properties;
    namespace System.Web.Mvc
    {
        /// <summary>Represents a class that is responsible for dynamically building a controller.</summary>
        public class ControllerBuilder
        {
            private static ControllerBuilder _instance = new ControllerBuilder();
            private Func<IControllerFactory> _factoryThunk = () => null;
            private HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            private IResolver<IControllerFactory> _serviceResolver;
            /// <summary>Gets the current controller builder object.</summary>
            /// <returns>The current controller builder.</returns>
            public static ControllerBuilder Current
            {
                get
                {
                    return ControllerBuilder._instance;
                }
            }
            /// <summary>Gets the default namespaces.</summary>
            /// <returns>The default namespaces.</returns>
            public HashSet<string> DefaultNamespaces
            {
                get
                {
                    return this._namespaces;
                }
            }
            /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerBuilder" /> class.</summary>
            public ControllerBuilder() : this(null)
            {
            }
            internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
            {
                IResolver<IControllerFactory> arg_6A_1 = serviceResolver;
                if (serviceResolver == null)
                {
                    arg_6A_1 = new SingleServiceResolver<IControllerFactory>(() => this._factoryThunk(), new DefaultControllerFactory
                    {
                        ControllerBuilder = this
                    }, "ControllerBuilder.GetControllerFactory");
                }
                this._serviceResolver = arg_6A_1;
            }
            /// <summary>Gets the associated controller factory.</summary>
            /// <returns>The controller factory.</returns>
            public IControllerFactory GetControllerFactory()
            {
                return this._serviceResolver.Current;
            }
            /// <summary>Sets the specified controller factory.</summary>
            /// <param name="controllerFactory">The controller factory.</param>
            /// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerFactory" /> parameter is null.</exception>
            public void SetControllerFactory(IControllerFactory controllerFactory)
            {
                if (controllerFactory == null)
                {
                    throw new ArgumentNullException("controllerFactory");
                }
                this._factoryThunk = (() => controllerFactory);
            }
            /// <summary>Sets the controller factory by using the specified type.</summary>
            /// <param name="controllerFactoryType">The type of the controller factory.</param>
            /// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerFactoryType" /> parameter is null.</exception>
            /// <exception cref="T:System.ArgumentException">The controller factory cannot be assigned from the type in the <paramref name="controllerFactoryType" /> parameter.</exception>
            /// <exception cref="T:System.InvalidOperationException">An error occurred while the controller factory was being set.</exception>
            public void SetControllerFactory(Type controllerFactoryType)
            {
                if (controllerFactoryType == null)
                {
                    throw new ArgumentNullException("controllerFactoryType");
                }
                if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType))
                {
                    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_MissingIControllerFactory, new object[]
                    {
                        controllerFactoryType
                    }), "controllerFactoryType");
                }
                this._factoryThunk = delegate
                {
                    IControllerFactory result;
                    try
                    {
                        result = (IControllerFactory)Activator.CreateInstance(controllerFactoryType);
                    }
                    catch (Exception innerException)
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_ErrorCreatingControllerFactory, new object[]
                        {
                            controllerFactoryType
                        }), innerException);
                    }
                    return result;
                }
                ;
            }
        }
    }
    DefaultControllerFactory.cs
    
        public virtual IController CreateController(RequestContext requestContext, string controllerName)
            {
                if (requestContext == null)
                {
                    throw new ArgumentNullException("requestContext");
                }
                if (string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())
                {
                    throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
                }
                Type controllerType = this.GetControllerType(requestContext, controllerName);
                return this.GetControllerInstance(requestContext, controllerType);
            }
    System.Web.Mvc.Controller
    
        protected override void ExecuteCore()
            {
                this.PossiblyLoadTempData();
                try
                {
                    string actionName = Controller.GetActionName(this.RouteData);
                    if (!this.ActionInvoker.InvokeAction(base.ControllerContext, actionName))
                    {
                        this.HandleUnknownAction(actionName);
                    }
                }
                finally
                {
                    this.PossiblySaveTempData();
                }
            }
    ControllerActionInvoker
    
    
    public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
            {
                if (controllerContext == null)
                {
                    throw new ArgumentNullException("controllerContext");
                }
                if (string.IsNullOrEmpty(actionName) && !controllerContext.RouteData.HasDirectRouteMatch())
                {
                    throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
                }
                ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(controllerContext);
                ActionDescriptor actionDescriptor = this.FindAction(controllerContext, controllerDescriptor, actionName);
                if (actionDescriptor != null)
                {
                    FilterInfo filters = this.GetFilters(controllerContext, actionDescriptor);
                    try
                    {
                        AuthenticationContext authenticationContext = this.InvokeAuthenticationFilters(controllerContext, filters.AuthenticationFilters, actionDescriptor);
                        if (authenticationContext.Result != null)
                        {
                            AuthenticationChallengeContext authenticationChallengeContext = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, authenticationContext.Result);
                            this.InvokeActionResult(controllerContext, authenticationChallengeContext.Result ?? authenticationContext.Result);
                        }
                        else
                        {
                            AuthorizationContext authorizationContext = this.InvokeAuthorizationFilters(controllerContext, filters.AuthorizationFilters, actionDescriptor);
                            if (authorizationContext.Result != null)
                            {
                                AuthenticationChallengeContext authenticationChallengeContext2 = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, authorizationContext.Result);
                                this.InvokeActionResult(controllerContext, authenticationChallengeContext2.Result ?? authorizationContext.Result);
                            }
                            else
                            {
                                if (controllerContext.Controller.ValidateRequest)
                                {
                                    ControllerActionInvoker.ValidateRequest(controllerContext);
                                }
                                IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor);
                                ActionExecutedContext actionExecutedContext = this.InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor, parameterValues);
                                AuthenticationChallengeContext authenticationChallengeContext3 = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, actionExecutedContext.Result);
                                this.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, authenticationChallengeContext3.Result ?? actionExecutedContext.Result);
                            }
                        }
                    }
                    catch (ThreadAbortException)
                    {
                        throw;
                    }
                    catch (Exception exception)
                    {
                        ExceptionContext exceptionContext = this.InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception);
                        if (!exceptionContext.ExceptionHandled)
                        {
                            throw;
                        }
                        this.InvokeActionResult(controllerContext, exceptionContext.Result);
                    }
                    return true;
                }
                return false;
            }
    
        protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
            {
                actionResult.ExecuteResult(controllerContext);
            }
     ViewResultBase
    public override void ExecuteResult(ControllerContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException("context");
                }
                if (string.IsNullOrEmpty(this.ViewName))
                {
                    this.ViewName = context.RouteData.GetRequiredString("action");
                }
                ViewEngineResult viewEngineResult = null;
                if (this.View == null)
                {
                    viewEngineResult = this.FindView(context);
                    this.View = viewEngineResult.View;
                }
                TextWriter output = context.HttpContext.Response.Output;
                ViewContext viewContext = new ViewContext(context, this.View, this.ViewData, this.TempData, output);
                this.View.Render(viewContext, output);
                if (viewEngineResult != null)
                {
                    viewEngineResult.ViewEngine.ReleaseView(context, this.View);
                }
            }

      在理解了上面的调用过程后,我们知道执行完Action后会调用ActionResult.ExecuteResult()方法去找View。那么在这个过程中,我们就可以扩展出自己的Result。具体该怎么做呢,我们只要继承ActionResult,重写它的 ExecuteResult 方法即可。

      在工作中我们,数据之间通常以XML格式或Json格式进行传输,所以接下来我们来实现下自己的XmlResult和JsonResult。

        /// <summary>
        /// 自定义XmlResult
        /// </summary>
        public class XmlResult : ActionResult
        {
            private object _data;
    
            public XmlResult(object data)
            {
                _data = data;
            }
    
            public override void ExecuteResult(ControllerContext context)
            {
                var serializer = new XmlSerializer(_data.GetType());
                var response = context.HttpContext.Response;
                response.ContentType = "text/xml";
                serializer.Serialize(response.Output, _data);
            }
        }
        /// <summary>
        /// 自定义NewtonSoft的JsonResult
        /// </summary>
        public class NewtonsoftJsonResult : ActionResult
        {
            private object _data = null;
    
            public NewtonsoftJsonResult(object data)
            {
                _data = data;
            }
    
            public override void ExecuteResult(ControllerContext context)
            {
                var response = context.HttpContext.Response;
                response.ContentType = "application/json";
                response.Write(JsonConvert.SerializeObject(this._data));
            }
        }

       当然,MVC框架中的ViewResult没有这么简单,它会根据Url去RouteData里找action得到view的名称,然后通过视图引擎ViewEngine去查找View。所以我们可以猜到MVC中的View实际上也会被编译成一个类,通过查看源码也证实了这一点。它调用BuildManager的静态方法GetCompiledType根据指定的View文件虚拟路径得到编译后的WebPageView类型,然后将改类型交给ViewPageActivator激活一个具体的WebPageView对象,并调用Render方法完成对View的最终呈现。

    using System;
    using System.Globalization;
    using System.IO;
    using System.Web.Mvc.Properties;
    namespace System.Web.Mvc
    {
        /// <summary>Represents the base class for views that are compiled by the BuildManager class before being rendered by a view engine.</summary>
        public abstract class BuildManagerCompiledView : IView
        {
            internal IViewPageActivator ViewPageActivator;
            private IBuildManager _buildManager;
            private ControllerContext _controllerContext;
            internal IBuildManager BuildManager
            {
                get
                {
                    if (this._buildManager == null)
                    {
                        this._buildManager = new BuildManagerWrapper();
                    }
                    return this._buildManager;
                }
                set
                {
                    this._buildManager = value;
                }
            }
            /// <summary>Gets or sets the view path.</summary>
            /// <returns>The view path.</returns>
            public string ViewPath
            {
                get;
                protected set;
            }
            /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.BuildManagerCompiledView" /> class using the specified controller context and view path.</summary>
            /// <param name="controllerContext">The controller context.</param>
            /// <param name="viewPath">The view path.</param>
            protected BuildManagerCompiledView(ControllerContext controllerContext, string viewPath) : this(controllerContext, viewPath, null)
            {
            }
            /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.BuildManagerCompiledView" /> class using the specified controller context, view path, and view page activator.</summary>
            /// <param name="controllerContext">Context information for the current controller. This information includes the HTTP context, request context, route data, parent action view context, and more.</param>
            /// <param name="viewPath">The path to the view that will be rendered.</param>
            /// <param name="viewPageActivator">The object responsible for dynamically constructing the view page at run time. </param>
            /// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerContext" /> parameter is null.</exception>
            /// <exception cref="T:System.ArgumentException">The <paramref name="viewPath" /> parameter is null or empty.</exception>
            protected BuildManagerCompiledView(ControllerContext controllerContext, string viewPath, IViewPageActivator viewPageActivator) : this(controllerContext, viewPath, viewPageActivator, null)
            {
            }
            internal BuildManagerCompiledView(ControllerContext controllerContext, string viewPath, IViewPageActivator viewPageActivator, IDependencyResolver dependencyResolver)
            {
                if (controllerContext == null)
                {
                    throw new ArgumentNullException("controllerContext");
                }
                if (string.IsNullOrEmpty(viewPath))
                {
                    throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewPath");
                }
                this._controllerContext = controllerContext;
                this.ViewPath = viewPath;
                this.ViewPageActivator = (viewPageActivator ?? new BuildManagerViewEngine.DefaultViewPageActivator(dependencyResolver));
            }
            /// <summary>Renders the specified view context by using the specified the writer object.</summary>
            /// <param name="viewContext">Information related to rendering a view, such as view data, temporary data, and form context.</param>
            /// <param name="writer">The writer object.</param>
            /// <exception cref="T:System.ArgumentNullException">The <paramref name="viewContext" /> parameter is null.</exception>
            /// <exception cref="T:SInvalidOperationException">An instance of the view type could not be created.</exception>
            public virtual void Render(ViewContext viewContext, TextWriter writer)
            {
                if (viewContext == null)
                {
                    throw new ArgumentNullException("viewContext");
                }
                object obj = null;
                Type compiledType = this.BuildManager.GetCompiledType(this.ViewPath);
                if (compiledType != null)
                {
                    obj = this.ViewPageActivator.Create(this._controllerContext, compiledType);
                }
                if (obj == null)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.CshtmlView_ViewCouldNotBeCreated, new object[]
                    {
                        this.ViewPath
                    }));
                }
                this.RenderView(viewContext, writer, obj);
            }
            /// <summary>When overridden in a derived class, renders the specified view context by using the specified writer object and object instance.</summary>
            /// <param name="viewContext">Information related to rendering a view, such as view data, temporary data, and form context.</param>
            /// <param name="writer">The writer object.</param>
            /// <param name="instance">An object that contains additional information that can be used in the view.</param>
            protected abstract void RenderView(ViewContext viewContext, TextWriter writer, object instance);
        }
    }

      ASP.NET MVC对View文件进行动态编译生成的类型名称基于View文件的虚拟路径,比如文件路径为“~/Views/Home/Index.cshtml”的View对应的类型为“ASP._Page_Views_Home_Index_cshtml”。它是按照目录进行编译的,“~/Views/Home/”下的View文件最终都被编译到一个程序集“App_Web_hufnk2d5”中。程序集按需加载,即第一次访问“~/View/Home/”目录下的View并不会加载针对“~/View/Order/”目录的程序集,而且实际上此时该程序集也尚未生成。

      理解了这个之后,我们便可以自定义View了。

          public void ViewTest()
            {
                ViewData.Model = new User
                {
                    Id = 1,
                    Name = "Jesen",
                    Email = "Jesen@126.com"
                };
                CustomView = new CustomView("~/Views/Home/ViewTest.cshtml");
                ViewContext viewContext = new ViewContext(ControllerContext, view, ViewData, TempData, Response.Output);
                view.Render(viewContext, viewContext.Writer);
            }
    
            public class CustomView : IView
            {
                public string ViewPath { get; private set; }
    
                public CustomView(string viewPath)
                {
                    this.ViewPath = viewPath;
                }
    
                public void Render(ViewContext viewContext, TextWriter writer)
                {
                    Type viewType = BuildManager.GetCompiledType(this.ViewPath);
                    object instance = Activator.CreateInstance(viewType);
                    WebViewPage page = (WebViewPage)instance as WebViewPage;
    
                    page.VirtualPath = this.ViewPath;
                    page.ViewContext = viewContext;
                    page.ViewData = viewContext.ViewData;
                    page.InitHelpers();
    
                    WebPageContext pageContext = new WebPageContext(viewContext.HttpContext, null, null);
                    WebPageRenderingBase startPage = StartPage.GetStartPage(page, "_ViewStart", new string[] { "cshtml" });
                    page.ExecutePageHierarchy(pageContext, writer, startPage);
                }
            }

      最后,既然是通过视图引擎去找的View,那么我们可不可以定义自己的视图引擎呢?答案是肯定的。MVC中提供了两种视图引擎,WebFormViewEngine 和 RazorViewEngine。

    using System;
    namespace System.Web.Mvc
    {
        /// <summary>Represents a collection of view engines that are available to the application.</summary>
        public static class ViewEngines
        {
            private static readonly ViewEngineCollection _engines = new ViewEngineCollection
            {
                new WebFormViewEngine(), 
                new RazorViewEngine()
            };
            /// <summary>Gets the view engines.</summary>
            /// <returns>The view engines.</returns>
            public static ViewEngineCollection Engines
            {
                get
                {
                    return ViewEngines._engines;
                }
            }
        }
    }

    因为现在很少使用WebForm,接下来来扩展下 RazorViewEngine。

    /// <summary>
        /// Razor视图引擎扩展
        /// </summary>
        public class CustomerViewEngine : RazorViewEngine
        {
            /// <summary>
            /// 可以分开部署不同语种
            /// </summary>
            /// <param name="engineName"></param>
            public CustomerViewEngine(string engineName)
            {
                base.ViewLocationFormats = new[]
                    {
                        "~/Views" + engineName + "/{1}/{0}.cshtml",
                        "~/Views" + engineName + "/Shared/{0}.cshtml"
                    };
    
                base.PartialViewLocationFormats = new[]
                    {
                        "~/Views" + engineName + "/{1}/{0}.cshtml",
                        "~/Views" + engineName + "/Shared/{0}.cshtml"
                    };
    
                base.AreaViewLocationFormats = new[]
                    {
                        "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml",
                        "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml"
                    };
    
                base.AreaPartialViewLocationFormats = new[]
                    {
                        "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml",
                        "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml"
                    };
            }
    
            public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
            {
                this.SetEngine(controllerContext);
                return base.FindView(controllerContext, viewName, masterName, useCache);
            }
    
            public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
            {
                this.SetEngine(controllerContext);
                return base.FindPartialView(controllerContext, partialViewName, useCache);
            }
    
            /// <summary>
            /// 根据条件自行设置,如果检测是移动端的就展示/Themes/Mobile下的/// 然后写入cookie
            /// </summary>
            private void SetEngine(ControllerContext controllerContext)
            {
                string engineName = "/Themes/Mobile";
          if (controllerContext.HttpContext.Request.IsMobile())//检测是不是移动端
                {
                    engineName = null;
                }
    
                base.ViewLocationFormats = new[]
                   {
                        "~/Views" + engineName + "/{1}/{0}.cshtml",
                        "~/Views" + engineName + "/Shared/{0}.cshtml"
                    };
    
                base.PartialViewLocationFormats = new[]
                    {
                        "~/Views" + engineName + "/{1}/{0}.cshtml",
                        "~/Views" + engineName + "/Shared/{0}.cshtml"
                    };
    
                base.AreaViewLocationFormats = new[]
                    {
                        "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml",
                        "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml"
                    };
    
                base.AreaPartialViewLocationFormats = new[]
                    {
                        "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml",
                        "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml"
                    };
            }
        }

    要是扩展的视图引擎生效需要在Application_Start中添加如下语句

      ViewEngines.Engines.Clear();//清空原有的视图引擎
      ViewEngines.Engines.Add(new CustomerViewEngine(""));//添加自己扩展的视图引擎

    至此,Asp.Net 从请求到视图的渲染过程就基本讲完了。

      

  • 相关阅读:
    .Net平台AOP技术概览
    ARP&ICMP
    .NET面向上下文、AOP架构模式(概述)
    Attribute在拦截机制上的应用
    .NET面向上下文、AOP架构模式(实现)
    使用RequireJS优化Web应用前端
    使用asp.net MVC4创建兼容各种设备显示的web应用程序
    entity framework for asp.net mvc
    jquery多功能软键盘插件
    优美登录页面源码(一)
  • 原文地址:https://www.cnblogs.com/jesen1315/p/11007044.html
Copyright © 2011-2022 走看看