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, -, 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();
}
/// <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 = ,
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);
}
}

Asp.Net MVC中Controller、Action、View是如何激活调用的

  最后,既然是通过视图引擎去找的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 从请求到视图的渲染过程就基本讲完了。

  

上一篇:【SSH 基础】浅谈Hibernate关系映射(4)


下一篇:[转]windows 短文件名/短路径名规则