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