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 从请求到视图的渲染过程就基本讲完了。

  

转载于:https://www.cnblogs.com/jesen1315/p/11007044.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值