前文简介:我们抽象类路由规则的对象,RouteBase是路由对象的抽象基类,ASP.NET 的路由系统中有唯一一个从RouteBase继承的路由对象,那就是Route类型了。我们注册了路由对象Route,UrlRoutingModule截获请求,把当前请求的Url地址和RouteTable路由表中注册的路由对象一个一个的比较,如果没有找到就返回Null,请求就到此终止了。如果有匹配的路由对象,选择第一个匹配的Route对象,并且根据该Route对象,生成了路由数据RouteData对象,本对象是为了封装成RequestContext对象,RequestContext对象封装了RouteData和HttpContext对象。

其实RequestContext对象也是作为参数使用的,我们根据Route对象获得了RouteData,该对象有一个RouteHandler属性,这个属性的值在ASP.NET MVC中是MvcRouteHandler。RouteHandler主要的作用是提供用于处理最终请求的HttpHandler对象,代码如下:

IHttpHandler=RouteData.RouteHandler.GetHttpHandler(requestContext);
HttpContext.Remap(IHttpHandler);

上文中我们获取到的RequestContext,将此作为参数调用RouteHandler的GetHttpHandler方法,我们获得了IHttpHandler对象,我们需要把请求交给这个HttpHandler来处理,通过HttpContext.Remap(Handler)实现请求的交接,这个Handler在ASP.NET MVC中就是MvcHandler,我们既然获得了HttpHandler对象,也实现了请求的交接,下一步该做什么呢?

一、概览

我们获得了用于处理请求HttpHandler对象,下一步最重要的任务是把Controller对象找到,RouteData对象的Values属性的Controller键和Action键的值只是一个字符串的结果,我们要通过这个名称找到具体的Controller类型实例,才能执行Action方法,给客户想要的应答。这篇文章就是Controller的激活方法的详解。

ASP.NET MVC系统真的很庞大,也很复杂,如果我们一上来就瀑布式的说,就像流水账一样,估计也不太容易说明白。为了便于理解,我们人为的把ASP.NET MVC这么庞大的系统拆分成相互独立,又有一定联系的多个子系统,然后我们各个击破,理解起来也就简单了,最后在把各个部分整合在一起,理解就全面了。今天我们要说的这一部分暂时叫做Controller激活系统吧,激活系统有两个含义,一是我们要找到我们需要的Controller对象,并实例化;二是我们药缓存他,并要执行它。

我们先来看看MvcHandler的源码吧,有助于我们的理解,代码如下:

         /// <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;
} /// <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>
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 && ValidationUtility.IsValidationEnabled(current) == true)
{
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);
} /// <summary>Enables processing of HTTP Web requests by a custom HTTP handler that implements the <see cref="T:System.Web.IHttpHandler" /> interface.</summary>
/// <param name="httpContext">An <see cref="T:System.Web.HttpContext" /> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) that are used to service HTTP requests.</param>
void IHttpHandler.ProcessRequest(HttpContext httpContext)
{
this.ProcessRequest(httpContext);
} /// <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="context">The HTTP context.</param>
/// <param name="cb">The asynchronous callback method.</param>
/// <param name="extraData">The data.</param>
IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
return this.BeginProcessRequest(context, cb, extraData);
} /// <summary>Called by ASP.NET when asynchronous request processing has ended.</summary>
/// <param name="result">The asynchronous result.</param>
void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
{
this.EndProcessRequest(result);
}
}

MvcHandler里面这个两个方法定义了Controller激活系统实现的骨架:

         /// <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 && ValidationUtility.IsValidationEnabled(current) == true)
{
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
}));
}
}

代码不是很难,大家一看就能看懂,下一节好好的介绍一些Controller激活系统中所涉及到的各个对象,具体的解析规则要到下一篇文章了,否则文章就太长了。

二、Controller对象激活详述

ASP.NET MVC中,我们人为分解的当前系统叫:Controller激活系统,其实这个激活系统里面包含的对象也不是很多,主要有我们要实例化的Controller对象,管理Controller的ControllerFactory对象,管理ControllerFactory的ControllerBuilder对象,这三个主要对象构成了我们的激活系统。

1、我们先看看Controller类型的定义吧

我们所说的Controller其实是指实现了IController接口的某个类型实例。Controller是一个可以执行的对象,它的执行体现在对Execute方法上的调用。一说到执行我们是不是就会想到同步执行和异步执行呢,既然Controller是一个可以执行的对象,他会不会也具有同步和异步的执行呢,答案是肯定的,我们先来看看两个接口吧:

public interface IController
{
/// <summary>Executes the specified request context.</summary>
/// <param name="requestContext">The request context.</param>
void Execute(RequestContext requestContext);
}

这个就是同步版的接口,该接口的命名空间是:System.Web.Mvc,当目标Controller对象被成功激活后,对请求的后续处理和最终响应都通过执行者Execute方法来实现。我们再看看异步版的接口定义吧:

 public interface IAsyncController : IController
{
/// <summary>Executes the specified request context.</summary>
/// <returns>The status of the asynchronous operation.</returns>
/// <param name="requestContext">The request context.</param>
/// <param name="callback">The asynchronous callback method.</param>
/// <param name="state">The state.</param>
IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state); /// <summary>Ends the asynchronous operation.</summary>
/// <param name="asyncResult">The asynchronous result.</param>
void EndExecute(IAsyncResult asyncResult);
}

异步版接口定义的命名空间是:System.Web.Mvc.Async,我们看到IAsyncController是实现了IController的接口,Controller的异步执行是通过调用BeginExecute和EndExecute方法来实现的。我们通过Visual Studio创建的ASP.NET MVC项目,我们自定义的Controller实现的基类是Controller类型,不是直接实现以上接口的。我们看看Controller类型的定义吧:

public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IController, IAsyncManagerContainer
{
//代码省略
}

Controller是抽象类型,实现了很多接口,因为该类的代码太多,所以省略了部分内部代码,这个里面牵扯到另外一个类,就是ControllerBase,

 /// <summary>Represents the base class for all MVC controllers.</summary>
public abstract class ControllerBase : IController
{
private readonly SingleEntryGate _executeWasCalledGate = new SingleEntryGate(); private DynamicViewDataDictionary _dynamicViewDataDictionary; private TempDataDictionary _tempDataDictionary; private bool _validateRequest = true; private IValueProvider _valueProvider; private ViewDataDictionary _viewDataDictionary; /// <summary>Gets or sets the controller context.</summary>
/// <returns>The controller context.</returns>
public ControllerContext ControllerContext
{
get;
set;
} /// <summary>Gets or sets the dictionary for temporary data.</summary>
/// <returns>The dictionary for temporary data.</returns>
public TempDataDictionary TempData
{
get
{
if (this.ControllerContext != null && this.ControllerContext.IsChildAction)
{
return this.ControllerContext.ParentActionViewContext.TempData;
}
if (this._tempDataDictionary == null)
{
this._tempDataDictionary = new TempDataDictionary();
}
return this._tempDataDictionary;
}
set
{
this._tempDataDictionary = value;
}
} /// <summary>Gets or sets a value that indicates whether request validation is enabled for this request.</summary>
/// <returns>true if request validation is enabled for this request; otherwise, false. The default is true.</returns>
public bool ValidateRequest
{
get
{
return this._validateRequest;
}
set
{
this._validateRequest = value;
}
} /// <summary>Gets or sets the value provider for the controller.</summary>
/// <returns>The value provider for the controller.</returns>
public IValueProvider ValueProvider
{
get
{
if (this._valueProvider == null)
{
this._valueProvider = ValueProviderFactories.Factories.GetValueProvider(this.ControllerContext);
}
return this._valueProvider;
}
set
{
this._valueProvider = value;
}
} /// <summary>Gets the dynamic view data dictionary.</summary>
/// <returns>The dynamic view data dictionary.</returns>
[Dynamic]
public dynamic ViewBag
{
[return: Dynamic]
get
{
if (this._dynamicViewDataDictionary == null)
{
this._dynamicViewDataDictionary = new DynamicViewDataDictionary(() => this.ViewData);
}
return this._dynamicViewDataDictionary;
}
} /// <summary>Gets or sets the dictionary for view data.</summary>
/// <returns>The dictionary for the view data.</returns>
public ViewDataDictionary ViewData
{
get
{
if (this._viewDataDictionary == null)
{
this._viewDataDictionary = new ViewDtaDictionary();
}
return this._viewDataDictionary;
}
set
{
this._viewDataDictionary = value;
}
}
/// <summary>Executes the specified request context.</summary>
/// <param name="requestContext">The request context.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception>
protected virtual void Execute(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (requestContext.HttpContext == null)
{
throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
}
this.VerifyExecuteCalledOnce();
this.Initialize(requestContext);
using (ScopeStorage.CreateTransientScope())
{
this.ExecuteCore();
}
} /// <summary>Executes the request.</summary>
protected abstract void ExecuteCore(); /// <summary>Initializes the specified request context.</summary>
/// <param name="requestContext">The request context.</param>
protected virtual void Initialize(RequestContext requestContext)
{
this.ControllerContext = new ControllerContext(requestContext, this);
} internal void VerifyExecuteCalledOnce()
{
if (!this._executeWasCalledGate.TryEnter())
{
string message = string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBase_CannotHandleMultipleRequests, new object[]
{
base.GetType()
});
throw new InvalidOperationException(message);
}
} /// <summary>Executes the specified request context.</summary>
/// <param name="requestContext">The request context.</param>
void IController.Execute(RequestContext requestContext)
{
this.Execute(requestContext);
}
}

ControllerBase实现了IController接口,但是没有实现IAsyncController接口,说明ControllerBase是一个同步的基类,之所以单独又增加ControllerBase,个人认为它定义了方法调用的扩展点。ControllerBase是一个抽象类,通过显示的实现了IController的Execute方法,在这个显示实现的方法IController.Execute方法里面又调用了受保护的虚方法Execute,这个受保护的虚方法Execute又在内部调用了抽象方法ExecuteCore,作为ControllerBase的继承者,必须通过实现对抽象方法ExecuteCore来完成对Controller的执行。这个过程就是方法调用的扩展点,而且ControllerBase也抽象所有Controller中都会用到的一些共同的属性,如TempData,ControllerContext,ValueProvider,ViewBag,ViewData等,大家可以细细研究,这里就不细说了,都很简单。

在ControllerBase里面有一个初始化的方法,这个方法的作用是根据当前Controller对象和当前请求上下文RequestContext创建ControllerContext对象,在请求后续处理的时候很多地方会用到ControllerContext。

/// <summary>Initializes the specified request context.</summary>
/// <param name="requestContext">The request context.</param>
protected virtual void Initialize(RequestContext requestContext)
{
this.ControllerContext = new ControllerContext(requestContext, this);
}

此方法是虚方法,我们可以扩展他,这个方法的调用点在受保护的虚方法Exuecute中,在调用ExecuteCore之前调用了该方法。

我们说了接口IController,IAsyncController,也说了抽象类ControllerBase,我们最后对我们要经常使用的基类做一下简述,我们真正在项目中使用的Controller的基类型是Controller类型,Controller实现了IController接口和IAsyncController接口,说明我们自己的Controller是可以同步执行,也可以异步执行的,同时实现了IDisposable接口,说明Controller类型是需要释放的,谁来释放激活的Controller的类型呢,那就是我们接下来要说的一个对象ControllerFactory,这里不多说,下面会解释清楚的。

最后我们需要说明的,虽然Controller可以同步执行,也可以异步执行,但是是同步还是异步是靠DisableAsyncSupport属性控制的,返回的是布尔值,默认情况是false,意思是指支持异步执行,以下代码说明实际情况。当BeginExecute方法执行的时候,它会根据该属性的值决定调用Execute同步执行Controller,还是调用BeginExecuteCore/EndExecuteCore方法异步执行Controller,换句话说,如果总是希望Controller同步执行,那就要把DisableAsyncSupport属性设置为true。

 protected virtual IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state)
{
if (this.DisableAsyncSupport)
{
Action action = delegate
{
this.Execute(requestContext);
};
return AsyncResultWrapper.BeginSynchronous(callback, state, action, Controller._executeTag);
}
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
this.Initialize(requestContext);
BeginInvokeDelegate<Controller> beginDelegate = (AsyncCallback asyncCallback, object callbackState, Controller controller) => controller.BeginExecuteCore(asyncCallback, callbackState);
EndInvokeVoidDelegate<Controller> endDelegate = delegate(IAsyncResult asyncResult, Controller controller)
{
controller.EndExecuteCore(asyncResult);
};
return AsyncResultWrapper.Begin<Controller>(callback, state, beginDelegate, endDelegate, this, Controller._executeTag, -, null);
}

2、ControllerContext

在ASP.NET MVC中我们会遇到很多上下文(Context)的类型,比如:RequestContext,它是对HttpContext和RouteData的封装,今天我们看看ControllerContext,就是指Controller的上下文,再说明白一点就是Controller在执行过程中对需要用到的一些数据的封装对象,它类似一个Facade,里面包含了一些在Controller执行时所要用的一些对象,必须表示请求的HttpContextBase对象。这个类型没什么可说的,很简单,我把反编译的代码也都贴出来了,大家可以自己看。

 /// <summary>Encapsulates information about an HTTP request that matches specified <see cref="T:System.Web.Routing.RouteBase" /> and <see cref="T:System.Web.Mvc.ControllerBase" /> instances.</summary>
public class ControllerContext
{
private sealed class EmptyHttpContext : HttpContextBase
{
} internal const string ParentActionViewContextToken = "ParentActionViewContext"; private HttpContextBase _httpContext; private RequestContext _requestContext; private RouteData _routeData; /// <summary>Gets or sets the controller.</summary>
/// <returns>The controller.</returns>
public virtual ControllerBase Controller
{
get;
set;
} /// <summary>Gets the display mode.</summary>
/// <returns>The display mode.</returns>
public IDisplayMode DisplayMode
{
get
{
return DisplayModeProvider.GetDisplayMode(this.HttpContext);
}
set
{
DisplayModeProvider.SetDisplayMode(this.HttpContext, value);
}
} /// <summary>Gets or sets the HTTP context.</summary>
/// <returns>The HTTP context.</returns>
public virtual HttpContextBase HttpContext
{
get
{
if (this._httpContext == null)
{
this._httpContext = ((this._requestContext != null) ? this._requestContext.HttpContext : new ControllerContext.EmptyHttpContext());
}
return this._httpContext;
}
set
{
this._httpContext = value;
}
} /// <summary>Gets a value that indicates whether the associated action method is a child action.</summary>
/// <returns>true if the associated action method is a child action; otherwise, false.</returns>
public virtual bool IsChildAction
{
get
{
RouteData routeData = this.RouteData;
return routeData != null && routeData.DataTokens.ContainsKey("ParentActionViewContext");
}
} /// <summary>Gets an object that contains the view context information for the parent action method.</summary>
/// <returns>An object that contains the view context information for the parent action method.</returns>
public ViewContext ParentActionViewContext
{
get
{
return this.RouteData.DataTokens["ParentActionViewContext"] as ViewContext;
}
} /// <summary>Gets or sets the request context.</summary>
/// <returns>The request context.</returns>
public RequestContext RequestContext
{
get
{
if (this._requestContext == null)
{
HttpContextBase httpContext = this.HttpContext ?? new ControllerContext.EmptyHttpContext();
RouteData routeData = this.RouteData ?? new RouteData();
this._requestContext = new RequestContext(httpContext, routeData);
}
return this._requestContext;
}
set
{
this._requestContext = value;
}
} /// <summary>Gets or sets the URL route data.</summary>
/// <returns>The URL route data.</returns>
public virtual RouteData RouteData
{
get
{
if (this._routeData == null)
{
this._routeData = ((this._requestContext != null) ? this._requestContext.RouteData : new RouteData());
}
return this._routeData;
}
set
{
this._routeData = value;
}
} /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class.</summary>
public ControllerContext()
{
} /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class by using the specified controller context.</summary>
/// <param name="controllerContext">The controller context.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerContext" /> parameter is null.</exception>
protected ControllerContext(ControllerContext controllerContext)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
this.Controller = controllerContext.Controller;
this.RequestContext = controllerContext.RequestContext;
} /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class by using the specified HTTP context, URL route data, and controller.</summary>
/// <param name="httpContext">The HTTP context.</param>
/// <param name="routeData">The route data.</param>
/// <param name="controller">The controller.</param>
public ControllerContext(HttpContextBase httpContext, RouteData routeData, ControllerBase controller) : this(new RequestContext(httpContext, routeData), controller)
{
} /// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.ControllerContext" /> class by using the specified request context and controller.</summary>
/// <param name="requestContext">The request context.</param>
/// <param name="controller">The controller.</param>
/// <exception cref="T:System.ArgumentNullException">One or both parameters are null.</exception>
public ControllerContext(RequestContext requestContext, ControllerBase controller)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (controller == null)
{
throw new ArgumentNullException("controller");
}
this.RequestContext = requestContext;
this.Controller = controller;
}
}

3、ControllerFactory

我们的Controller对象有了,为了解耦和可扩展性,ASP.NET MVC 框架中不可能直接new一个Controller的实例对象吧,我们在MvcHandler的代码中也看到了是基于接口编程的,所以需要提供一种机制,来提供我们要得到的Controller对象。既然不能直接new一个Controller出来,那我们就封装new的过程,用工厂来代替吧。对了,就是工厂,这个工厂就是ControllerFactory,从名字上可以看出它的意思,创建Controller对象的工厂。ASP.NET MVC是可以扩展的,可以自定义的,我们的ControllerFactory工厂也不例外,所以我们就产生了ControllerFactory工厂的接口,接口的名字是IControllerFactory,所有的ControllerFactory都必须实现该接口。接口定义如下:

       /// <summary>Defines the methods that are required for a controller factory.</summary>
public interface IControllerFactory
{
/// <summary>Creates the specified controller by using the specified request context.</summary>
/// <returns>The controller.</returns>
/// <param name="requestContext">The request context.</param>
/// <param name="controllerName">The name of the controller.</param>
IController CreateController(RequestContext requestContext, string controllerName); /// <summary>Gets the controller's session behavior.</summary>
/// <returns>The controller's session behavior.</returns>
/// <param name="requestContext">The request context.</param>
/// <param name="controllerName">The name of the controller whose session behavior you want to get.</param>
SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName); /// <summary>Releases the specified controller.</summary>
/// <param name="controller">The controller.</param>
void ReleaseController(IController controller);
}

IControllerFactory接口是对管理Controller对象的工厂的抽象,怎么管理呢,无非就是我要创建一个Controller就能创建一个Controller。我们创建好了Controller,不使用的情况就要能销毁它,能创建和能销毁就构成了IControllerFactory的主要功能,CreateController用于创建Controller对象实例,ReleaseController方法用于销毁不用的Controller对象,因为我们的Controller实现了IDisposable接口。想起来了吗?因为我们自定义的Controller都继承了抽象基类Controller类型,该抽象Controller基类实现了IDisposable接口。

IControllerFactory接口还有一个方法GetControllerSessionBehavior,用于管理Controller的会话状态,返回的类型为SessionStateBehavior枚举值,它有四个枚举值:Default(使用默认的ASP.NET逻辑来确定请求的会话行为),Required(为请求启用完全的读和写会话状态的行为),ReadOnly(为请求启用只读的会话状态),Disabled(禁用会话状态)。说起来话长,在System.Web.SessionState命名空间下有两个接口定义,分别是IRequiresSessionState(实现本接口,HttpHandler采用Required会话模式)和IReadOnlySessionState(实现本接口,HttpHandler采用ReadOnly会话模式)类型,这个两个接口是标记接口,没有定义具体的方法。我们在看看我们的MvcHandler类型的定义吧,代码如下:

public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState

具体采用何种会话状态行为取决于当前Http请求上下文(HttpContext的静态属性Current表示的对象),对于ASP.NET 3.0及其以前的版本,我们不能对当前的Http上下文的会话状态行为模式进行修改,在ASP.NET 4.0中为HttpContext对象定义了一个SetSessionStateBehavior方法,此方法也在HttpContextBase定义了,HttpContextBase的子类HttpContextWrapper重写了这个方法,在内部调用HttpContext的同名方法来设置当前请求的会话状态模式。

4、ControllerBuilder

我们有了Controller,可以通过IController,IAsyncController,ControllerBase或者Controller抽象基类来自定义我们自己的Controller基类型,我们也有了IControllerFactory接口,可以管理Controller对象,也可以定义我们自己的ControllerFactory来取代系统的默认实现。问题来了,我们自定义了ControllerFactory对象,但是如何把我们自定义的ControllerFactory放到ASP.NET MVC框架中呢,那就需要我们这个类型了,它的名字就是ControllerBuilder。

代码最有说服力,我们先上代码吧,源码如下:

      /// <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;
};
}
}

源码不是很多,我贴出了完整的代码。

ControllerBuilder定义了一个静态只读属性Current,返回当前使用的ControllerBuilder对象。该类型有一个返回类型为IControllerFactory的方法GetControllerFactory,这个方法用于获取当前注册的ControllerFactory对象。ControllerBuilder类型还有两个重载的SetControllerFactory方法,这两个方法的主要作用是把我们自定义的ControllerFactory注册到ASP.NET MVC框架中去。但是这两个方法有些不同,参数类型为IControllerFactory的SetControllerFactory方法直接注册ControllerFactory实例对象,而参数类型为Type的SetControllerFactory方法注册的ControllerFactory的类型。

如果我们注册的是ControllerFactory类型的话,那么GetControllerFactory方法在获取ControllerFactory对象的时候都要通过反射来获得,也就是说针对GetControllerFactory方法的每次调用都会伴随着ControllerFactory的实例化,ASP.NET MVC框架不会对实例化的ControllerFactory进行缓存。如果我们注册的是ControllerFactory对象实例,针对GetControllerFactory方法来说是直接返回注册的对象,就性能而言,注册ControllerFactory对象比较好。

命名空间

       如果在多个命名空间下定义了多个同名的Controller类型,如果只是按着名称来匹配就会导致激活系统无法确定具体的Controller的类型而抛出异常。为了解决这个问题,我们必须为定义了同名的Controller类型的命名空间设置不同的优先级。

ASP.NET MVC的Controller的激活系统为我们提供了两种提升命名空间优先级的方法。第一种方法就是通过调用RouteCollectionExtensions的扩展方法MapRoute的时候提供命名空间列表。此种方式指定的命名空间列表会保存在Route路由对象的DataTokens属性中,对应的Key是Namespaces。第二种方式就是把命名空间列表添加到当前的ControllerBuilder类型的默认命名空间列表中。这些命名空间列表存放在返回类型为HashSet<string>的DefaultNamespaces属性中。这两种方法的优先级第一种方法更高。

对于Area的命名空间来说,如果我们在调用AreaRegistrationContext对象的MapRoute方法时提供了命名空间,该命名空间会作为Route对象的命名空间,如果没有提供,则AreaRegistration类型所在的命名空间,再加上“.*”作为Route对象的命名空间。当我们调用AreaRegistrationContext的MapRoute方法的时候,会在Route对象的DataTokens属性里增加一个Key为UseNamespaceFallback的变量,它表示是否采用后备的命名空间来解析Controller类型。如果Route对象有命名空间,该值就是False,否则就是true。

解析过程是,ASP.NET MVC会先使用RouteData包含的命名空间,如果解析失败,它会从RouteData对象的DataTokens属性中取出Key为UseNamespaceFallback的值,如果该值为true或者不存在,就使用ControllerBuidler的默认命名空列表来解析。如果UseNamespaceFallback的值为false,就不实用后备的命名空间,如果没找到就会抛出异常。

三、激活Controller类型的缓存和释放

一个比较大的系统里面,可能所涉及到的Controller类型成千上万,如果每次都要通过反射的方式及时获取,那对性能的影响是很客观的,ASP.NET MVC框架组想到了这一点,针对Controller和AreaRegistration都实现了彻底的缓存,这个缓存是把所有解析出来的类型持久化到物理文件中,两个物理文件的名称分别是:MVC-ControllerTypeCache.xml和MVC-AreaRegistrationTypeCache.xml,具体的目录在%windir%\Microsoft.Net\Framework\v{version}\Temporary ASP.NET Files\root\..\..\UserCache\。

当接收到web应用被启动后的第一个请求的时候,Controller激活系统会读取这些文件,通过反序列化生成List<Type>对象。

MVC-ControllerTypeCache.xml代码如下:

<?xml version="1.0" encoding="UTF-8"?>

<!--This file is automatically generated. Please do not modify the contents of this file.-->

-<typeCache mvcVersionId="cc73190b-ab9d-435c-8315-10ff295c572a" lastModified="2017/6/23 18:34:17">

-<assembly name="MVCTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">

-<module versionId="1949a001-c30a-4d56-ac65-d1714f608b76">

<type>MVCTest.Controllers.AccountController</type>

<type>MVCTest.Controllers.HomeController</type>

<type>MVCTest.Controllers.ManageController</type>

</module>

</assembly>

</typeCache>

Controller的释放很简单,直接上代码吧。

/// <summary>Releases the specified controller.</summary>
/// <param name="controller">The controller to release.</param>
public virtual void ReleaseController(IController controller)
{
IDisposable disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}

由于所有的Controller类型都实现了IDisposable接口,所以我们可以直接调用当前Controller对象的Dispose方法即可。

四、小结

   好了,这篇文章就先写到这里吧,Controller的激活系统还没有完,今天只是从总体上来讲一下,内容很多,很难在一片文章里把所有东西都写到。这个概览先给大家一个整体上的感觉吧,下一篇文章就具体写一下Controller的激活解析过程。

写一写很不错,把自己的理解写出来,现在更清晰了,也希望高手来指正和赐教。

白话ASP.NET MVC之二:Controller激活系统的概览的更多相关文章

  1. 白话ASP.NET MVC之三:Controller是如何解析出来的

    我们在上一篇文章中介绍Controller激活系统中所涉及到的一些类型,比如有关Controller类型的相关定义类型就包括了IController类型,IAsyncController类型,Cont ...

  2. ASP.NET MVC Controller激活系统详解1

    一.引言 好久没有写博客了,前一段时间学习了Controller激活的一篇很好的博文(链接),在此做个学习总结. 二.Controller 2.1 IController Controller类型直接 ...

  3. ASP.NET MVC 路由(二)

     ASP.NET MVC路由(二) 前言 在上一篇中,提及了Route.RouteCollection对象的一些信息,以及它们的结构所对应的关系.按照处理流程走下来还有遗留的疑问没有解决这个篇幅就来讲 ...

  4. Asp.Net MVC<六>:Controller、Action 待续

    控制器 抽象类Controller Visual Studio的向导创建的Controller类型继承自抽象类Controller. 它是ControllerBase的子类. 实现了IControll ...

  5. ASP.NET MVC 视图(二)

    ASP.NET MVC 视图(二) 前言 上篇中对于视图引擎只是做了简单的演示,对于真正的理解视图引擎的工作过程可能还有点模糊,本篇将会对由MVC框架提供给我们的Razor视图引擎的整个执行过程做一个 ...

  6. ASP.NET MVC 3.0 Controller基础

    ASP.NET MVC 3.0 Controller基础   1.Controller类与方法 Controller(控制器)是ASP.NET MVC的核心,负责处理浏览器请求,并作出响应.Cotro ...

  7. Asp.net mvc项目架构分享系列之架构概览

    Asp.net mvc项目架构分享系列之架构概览 Contents 系列一[架构概览] 0.项目简介 1.项目解决方案分层方案 2.所用到的技术 3.项目引用关系 系列二[架构搭建初步] 4.项目架构 ...

  8. ASP.NET MVC Controller激活系统详解2

    一.引言 此篇博文紧接上篇博文进行阐述,本篇博文阐述的主题是Controller激活和url路由 二.总述 ASP.NET路由系统是HTTP请求抵达服务端的第一道屏障,它根据注册的路由规则对拦截的请求 ...

  9. Asp.net mvc 中的 Controller 的激活

    Controller 激活是指根据路由系统解析出来的 Controller 的名称创建 控制器(Controller)的过程,这里的控制器泛指实现了 IController 接口的类型 激活过程中的核 ...

随机推荐

  1. 打造一个简单的Java字节码反编译器

    简介 本文示范了一种反编译Java字节码的方法,首先通过解析class文件,然后将解析的结果转成java代码.但是本文并没有覆盖所有的class文件的特性和指令,只针对部分规范进行解析. 所有的代码代 ...

  2. 15.vue使用element-ui的el-input监听不了回车事件

    问题描述: 使用vue.js 2.0发现,el-input的绑定回车事件用不了 原因: el-input封了一层,直接@keyup.enter是用不了的 例如: <el-input type=& ...

  3. word2-寻找社交新浪微博中的目标用户

    项目简述: 为了进行更加精准的营销, 利用数据挖掘相关算法, 利用开放API或自行编写爬虫获得新浪微博, 知乎等社交网络(可能需要破解验证码)中用户所发布的数据, 利用数据挖掘的相关算法进行分析, 从 ...

  4. HybridApp Exception

    HybridApp Exception [创建安卓虚拟机失败]CPU acceleration status:HAXM must be updated(version 1.1.1<6.0.1) ...

  5. Tomcat7以上403 Access Denied错误

    版本:Tomcat 9 问题:新安装的tomcat,访问tomcat的Server Status.Manager App.Host Manager三个页面均显示403,conf/tomcat-user ...

  6. 简单总结一下 XSS

    你听说过XSS吗? XSS(Cross-site scripting, 跨站脚本)是一种网站应用程序的安全漏洞攻击,是代码注入的一种. 研究表明,最近几年 XSS 已经超过 "缓冲区溢出&q ...

  7. Java IO学习笔记(五)对象流

    1.Object流:直接将Object写入或读出. 2.序列化:将Object直接转化成字节流写到硬盘或网络上. 3.如果想把一个对象转化成字节流,该对象的实体类必须得实现Serializable接口 ...

  8. //读取配置文件(属性文件)的工具类-ConfigManager

    package com.pb.news.util; import java.io.IOException;import java.io.InputStream;import java.sql.Resu ...

  9. delphi选择打印机打印

    printer.getprinter(mdevice, mdriver, mport, mhdmode); printer.setprinter(pchar(g_PRNzt), mdriver, mp ...

  10. tp5实现邮件发送

    项目中,用户修改密码,需要发送验证码到用户邮箱,于是就看了看,在此记录一下. 1.开启SMTP服务 测试用的是自己的qq邮箱,首先需要开启邮箱的SMTP服务,开启之后,要记得给你的授权码,授权码一定要 ...