白话ASP.NET MVC之三:Controller是如何解析出来的
我们在上一篇文章中介绍Controller激活系统中所涉及到的一些类型,比如有关Controller类型的相关定义类型就包括了IController类型,IAsyncController类型,ControllerBase抽象类型和我们最终要使用的抽象类型Controller,这是ASP.NET MVC 框架中和Controller本身定义相关的类型。其他辅助类型,包括管理Controller的类型ControllerFactory,这个工厂负责Controller的生产和销毁。我们还涉及到另一个辅助类型,用于把系统默认定义的或者自定义的ControllerFactory注册到ASP.NET MVC框架中的类型ControllerBuilder。
Controller类型、ControllerFactory类型和ControllerBuilder类型,他们之间的关系可以描述为:ControllerBuilder是面向客户的,或者说是程序员和ASP.NET MVC框架之间的桥梁。我们通过ControllerBuilder类型的SetControllerFactory方法把我们自定义的ControllerFactory类型实例注册到ASP.NET MVC框架中,ControllerFactory类型用于管理Controller类型实例,其实也就是说ControllerFactory类型就是ASP.NET MVC框架中的一个扩展点。
我们今天主要讲Controller是怎么解析出来的,之所以把这一部分分开写,因为合在一起太长了,也说的不详细,如果大家对以上说的不太清楚,可以查看《白话ASP.NET MVC之二:Controller激活系统的概览》, 该文对ASP.NET MVC框架中所提到的Controlelr激活系统所涉及的类型有详细的介绍。
一、“路由系统”和“激活系统”是怎么关联起来的
上一篇文章有过讲述,我们在这里简单说一下。ASP.NET 的路由系统是建立在一个叫做UrlRoutingModule的HttpModule组件上的,针对请求的路由解析是通过注册HttpApplication对象的PostResolveRequestCache事件来实现的,为当前的请求动态映射到一个HttpHandler类型上,最终由该HttpHandler接管请求并处理。我们来看看UrlRoutingModule类型的代码吧。
public class UrlRoutingModule : IHttpModule
{
private static readonly object _contextKey = new object();
private static readonly object _requestDataKey = new object();
private RouteCollection _routeCollection;
public RouteCollection RouteCollection
{
get
{
if (this._routeCollection == null)
{
this._routeCollection = RouteTable.Routes;
}
return this._routeCollection;
}
set
{
this._routeCollection = value;
}
}
protected virtual void Dispose()
{
}
protected virtual void Init(HttpApplication application)
{
if (application.Context.Items[UrlRoutingModule._contextKey] != null)
{
return;
}
application.Context.Items[UrlRoutingModule._contextKey] = UrlRoutingModule._contextKey;
application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
}
private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
{
HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context);
this.PostResolveRequestCache(context);
}
[Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]
public virtual void PostMapRequestHandler(HttpContextBase context)
{
}
public virtual void PostResolveRequestCache(HttpContextBase context)
{
RouteData routeData = this.RouteCollection.GetRouteData(context);
if (routeData == null)
{
return;
}
IRouteHandler routeHandler = routeData.RouteHandler;
if (routeHandler == null)
{
]));
}
if (routeHandler is StopRoutingHandler)
{
return;
}
RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext;
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[]
{
routeHandler.GetType()
}));
}
if (!(httpHandler is UrlAuthFailureHandler))
{
context.RemapHandler(httpHandler);
return;
}
if (FormsAuthenticationModule.FormsAuthRequired)
{
UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
return;
}
, SR.GetString("Assess_Denied_Description3"));
}
void IHttpModule.Dispose()
{
this.Dispose();
}
void IHttpModule.Init(HttpApplication application)
{
this.Init(application);
}
}
具体来说,该组件通过以RouteTable的静态属性Routes表示的路由表针对当前请求实施路由解析,如果有匹配,就会根据路由对象Route来生成RouteData路由数据对象,然后我们借助RouteData对象的RouteHandler属性获取想要的HttpHandler对象。在默认情况下这个RouteHandler属性所代表的对象是MvcRouteHandler。翠花,上代码:
/// <summary>Creates an object that implements the IHttpHandler interface and passes the request context to it.</summary>
public class MvcRouteHandler : IRouteHandler
{
private IControllerFactory _controllerFactory;
/// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcRouteHandler" /> class.</summary>
public MvcRouteHandler()
{
}
/// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.MvcRouteHandler" /> class using the specified factory controller object.</summary>
/// <param name="controllerFactory">The controller factory.</param>
public MvcRouteHandler(IControllerFactory controllerFactory)
{
this._controllerFactory = controllerFactory;
}
/// <summary>Returns the HTTP handler by using the specified HTTP context.</summary>
/// <returns>The HTTP handler.</returns>
/// <param name="requestContext">The request context.</param>
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
/// <summary>Returns the session behavior.</summary>
/// <returns>The session behavior.</returns>
/// <param name="requestContext">The request context.</param>
protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
{
string text = (string)requestContext.RouteData.Values["controller"];
if (string.IsNullOrWhiteSpace(text))
{
throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);
}
IControllerFactory controllerFactory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
return controllerFactory.GetControllerSessionBehavior(requestContext, text);
}
/// <summary>Returns the HTTP handler by using the specified request context.</summary>
/// <returns>The HTTP handler.</returns>
/// <param name="requestContext">The request context.</param>
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
{
return this.GetHttpHandler(requestContext);
}
}
在该类型里面包含了一个IControllerFactory类型成员字段,这个接口类型是所有ControllerFactory都要必须实现的接口,否则就不叫Controller的工厂了。MvcRouteHandler类型有两个构造函数,无参的没的说,另一个需要传递一个IControllerFactory类型的参数,这个参数用于初始化MvcRouteHandler类型内部包含的类型为IControllerFactory的_controllerFactory字段。当我们构造MvcRouteHandler实例的时候,如果我们调用了无参的构造函数,它会在内部使用ControllerBuilder.Current.GetControllerFactory()方法来获取我们通过ControllerBuilder类型注册的IControllerFactory类型的实例,代码很明显:
IControllerFactory controllerFactory = this._controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
MvcRouteHandler实现了IRouteHandler接口,目的只有一个,提供后续的HttpHandler,IRouteHandler接口定义如下:
public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
MvcRouteHandler会给我们直接返回MvcHandler对象,这个对象用于处理请求,包括激活Controler对象,代码最有说服力,这份代码,上篇文章也贴过,现在也贴一下把,上代码:
/// <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();
, 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()
{
);
}
/// <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类型的BeginProcessRequest方法用来处理请求,包括激活Controller实例等。由于MvcHandler类型同时实现了IHttpHandler接口和IHttpAsyncHandler接口,所以他总是异步方式去执行(调用BeginProcessRequest/EndProcessRequest方法)。BeginProcessRequest方法在执行的时候通过RequestContext对象的RouteData属性获得Controller的名字,然后在通过ControllerBuilder获得ControllerFactory对象,然后用Controller的名字和ControllerFactory对象来激活目标Controller实例。如果Controller类型实现了IAsyncController接口,那就以异步方式执行Controller,否则以同步方式执行。Controller对象成功执行后,MvcHandler对象会调用ControllerFactory对象ReleaseController方法来销毁Controller实例对象。
我们小结一下,ASP.NET MVC的路由系统和Controller的激活系统是通过这些对象关联起来的:请求Url ------->Route------->RouteData------->RouteHandler(MvcRouteHandler)-------->MvcRouteHandler------>MvcHandler,通过这些对象就能串起来了。
二、Controller的详细解析过程
我先来简述一下Controller解析的原理吧。Controller实例对象的解析是通过实现了IControllerFactory接口的ControllerFactory对象实现的,ControllerFactory是怎么来的呢?是通过调用ControllerBuilder的SetControllerFactory方法实现对ControllerFactory类型或者实例对象的注册。如果我们没有调用ControllerBuilder的SetControllerFactory方法对象ControllerFactory类型或者实例显示注册,系统会使用默认的ControllerFactory来完成对Controller对象的解析,这个对象就是DefaultControllerFactory类型,该类型的实现正好反映了ASP.NET MVC框架对Controller实例的激活采取的默认策略。今天我们就看看DefaultControllerFactory类型是如何把Controller对象激活的,这也是Controller激活系统的默认实现,我们可以扩展ControllerFactory类型,实现自定义的Controller激活策略。
我把代码流程写一下,在MvcHandler类型里面有两个方法,一个方法是:BeginProcessRequest(HttpContextBase httpContext,AsyncCallback callback,object state),该方法用于对请求进行处理;第二个方法是:ProcessRequestInit(HttpContextbase httpContext,out IController controller,out IControllerFactory controllerFactory),该方法就是定义了激活Controller算法的骨架,上代码吧,代码最无二意,我们先看BeginProcessRequest的代码:
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();
, synchronizationContext);
}
Action action = delegate
{
try
{
controller.Execute(this.RequestContext);
}
finally
{
factory.ReleaseController(controller);
}
};
return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag);
}
在这个方法里面根据解析出来的Controller的类型来执行,如果是异步的Controller那就异步执行,否则就同步执行。在该方法里面,第三行,标红色的方法就是定义解析和执行Controller的算法骨架,就是我们要贴代码的第二个方法ProcessRequestInit,源码如下:
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");
11 factory = this.ControllerBuilder.GetControllerFactory();
12 controller = factory.CreateController(this.RequestContext, requiredString);
if (controller == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]
{
factory.GetType(),
requiredString
}));
}
}
这个方法主要是获取ControllerFactory实例,根据获得的ControllerFactory对象激活Controller对象,红色标注的代码就是核心关键点。说明一点,这个方法只是定义了激活Controller算法的骨架,具体的实现在DefaultControllerFactory类型中。代码很简单,我相信大家看的清楚。
DefaultControllerFactory通过调用BuildManager的静态方法GetReferencedAssemblies获取到系统所使用到的所有程序集,然后针对每个程序集通过反射的方式获得所有实现了IController接口的类型,并保存起来。然后我们把Controller的名称和命名空间作为匹配条件去查找对应的Controller类型。当我们获得了符合标准的真是的Controller类型后,DefaultControllerFactory对象通过反射的方式创建Controller类型的实例对象。解析逻辑不复杂,但是代码不少。我把完整的源码贴出来,大家可以仔细体会一下。
/// <summary>Represents the controller factory that is registered by default.</summary>
public class DefaultControllerFactory : IControllerFactory
{
private class DefaultControllerActivator : IControllerActivator
{
private Func<IDependencyResolver> _resolverThunk;
public DefaultControllerActivator() : this(null)
{
}
public DefaultControllerActivator(IDependencyResolver resolver)
{
if (resolver == null)
{
this._resolverThunk = (() => DependencyResolver.Current);
return;
}
this._resolverThunk = (() => resolver);
}
public IController Create(RequestContext requestContext, Type controllerType)
{
IController result;
try
{
result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
}
catch (Exception innerException)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[]
{
controllerType
}), innerException);
}
return result;
}
}
private static readonly ConcurrentDictionary<Type, SessionStateBehavior> _sessionStateCache = new ConcurrentDictionary<Type, SessionStateBehavior>();
private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache();
private IBuildManager _buildManager;
private IResolver<IControllerActivator> _activatorResolver;
private IControllerActivator _controllerActivator;
private ControllerBuilder _controllerBuilder;
private ControllerTypeCache _instanceControllerTypeCache;
private IControllerActivator ControllerActivator
{
get
{
if (this._controllerActivator != null)
{
return this._controllerActivator;
}
this._controllerActivator = this._activatorResolver.Current;
return this._controllerActivator;
}
}
internal IBuildManager BuildManager
{
get
{
if (this._buildManager == null)
{
this._buildManager = new BuildManagerWrapper();
}
return this._buildManager;
}
set
{
this._buildManager = value;
}
}
internal ControllerBuilder ControllerBuilder
{
get
{
return this._controllerBuilder ?? ControllerBuilder.Current;
}
set
{
this._controllerBuilder = value;
}
}
internal ControllerTypeCache ControllerTypeCache
{
get
{
return this._instanceControllerTypeCache ?? DefaultControllerFactory._staticControllerTypeCache;
}
set
{
this._instanceControllerTypeCache = value;
}
}
/// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class.</summary>
public DefaultControllerFactory() : this(null, null, null)
{
}
/// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.DefaultControllerFactory" /> class using a controller activator.</summary>
/// <param name="controllerActivator">An object that implements the controller activator interface.</param>
public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null)
{
}
internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
{
if (controllerActivator != null)
{
this._controllerActivator = controllerActivator;
return;
}
IResolver<IControllerActivator> arg_44_1 = activatorResolver;
if (activatorResolver == null)
{
arg_44_1 = new SingleServiceResolver<IControllerActivator>(() => null, new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory constructor");
}
this._activatorResolver = arg_44_1;
}
internal static InvalidOperationException CreateAmbiguousControllerException(RouteBase route, string controllerName, ICollection<Type> matchingTypes)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (Type current in matchingTypes)
{
stringBuilder.AppendLine();
stringBuilder.Append(current.FullName);
}
Route route2 = route as Route;
string message;
if (route2 != null)
{
message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithRouteUrl, new object[]
{
controllerName,
route2.Url,
stringBuilder,
Environment.NewLine
});
}
else
{
message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous_WithoutRouteUrl, new object[]
{
controllerName,
stringBuilder,
Environment.NewLine
});
}
return new InvalidOperationException(message);
}
private static InvalidOperationException CreateDirectRouteAmbiguousControllerException(ICollection<Type> matchingTypes)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (Type current in matchingTypes)
{
stringBuilder.AppendLine();
stringBuilder.Append(current.FullName);
}
string message = string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_DirectRouteAmbiguous, new object[]
{
stringBuilder,
Environment.NewLine
});
return new InvalidOperationException(message);
}
/// <summary>Creates the specified controller by using the specified request context.</summary>
/// <returns>The controller.</returns>
/// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
/// <param name="controllerName">The name of the controller.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception>
/// <exception cref="T:System.ArgumentException">The <paramref name="controllerName" /> parameter is null or empty.</exception>
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);
}
/// <summary>Retrieves the controller instance for the specified request context and controller type.</summary>
/// <returns>The controller instance.</returns>
/// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
/// <param name="controllerType">The type of the controller.</param>
/// <exception cref="T:System.Web.HttpException">
/// <paramref name="controllerType" /> is null.</exception>
/// <exception cref="T:System.ArgumentException">
/// <paramref name="controllerType" /> cannot be assigned.</exception>
/// <exception cref="T:System.InvalidOperationException">An instance of <paramref name="controllerType" /> cannot be created.</exception>
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
, string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound, new object[]
{
requestContext.HttpContext.Request.Path
}));
}
if (!typeof(IController).IsAssignableFrom(controllerType))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase, new object[]
{
controllerType
}), "controllerType");
}
return this.ControllerActivator.Create(requestContext, controllerType);
}
/// <summary>Returns the controller's session behavior.</summary>
/// <returns>The controller's session behavior.</returns>
/// <param name="requestContext">The request context.</param>
/// <param name="controllerType">The type of the controller.</param>
protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return SessionStateBehavior.Default;
}
return DefaultControllerFactory._sessionStateCache.GetOrAdd(controllerType, delegate(Type type)
{
SessionStateAttribute sessionStateAttribute = type.GetCustomAttributes(typeof(SessionStateAttribute), true).OfType<SessionStateAttribute>().FirstOrDefault<SessionStateAttribute>();
if (sessionStateAttribute == null)
{
return SessionStateBehavior.Default;
}
return sessionStateAttribute.Behavior;
});
}
/// <summary>Retrieves the controller type for the specified name and request context.</summary>
/// <returns>The controller type.</returns>
/// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
/// <param name="controllerName">The name of the controller.</param>
protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (string.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch()))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
RouteData routeData = requestContext.RouteData;
if (routeData != null && routeData.HasDirectRouteMatch())
{
return DefaultControllerFactory.GetControllerTypeFromDirectRoute(routeData);
}
object obj;
if (routeData.DataTokens.TryGetValue("Namespaces", out obj))
{
IEnumerable<string> enumerable = obj as IEnumerable<string>;
if (enumerable != null && enumerable.Any<string>())
{
HashSet<string> namespaces = new HashSet<string>(enumerable, StringComparer.OrdinalIgnoreCase);
Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces);
if (controllerTypeWithinNamespaces != null || false.Equals(routeData.DataTokens["UseNamespaceFallback"]))
{
return controllerTypeWithinNamespaces;
}
}
}
)
{
HashSet<string> namespaces2 = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces2);
if (controllerTypeWithinNamespaces != null)
{
return controllerTypeWithinNamespaces;
}
}
return this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null);
}
private static Type GetControllerTypeFromDirectRoute(RouteData routeData)
{
IEnumerable<RouteData> directRouteMatches = routeData.GetDirectRouteMatches();
List<Type> list = new List<Type>();
foreach (RouteData current in directRouteMatches)
{
if (current != null)
{
Type targetControllerType = current.GetTargetControllerType();
if (targetControllerType == null)
{
throw new InvalidOperationException(MvcResources.DirectRoute_MissingControllerType);
}
if (!list.Contains(targetControllerType))
{
list.Add(targetControllerType);
}
}
}
)
{
return null;
}
)
{
];
}
throw DefaultControllerFactory.CreateDirectRouteAmbiguousControllerException(list);
}
private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces)
{
this.ControllerTypeCache.EnsureInitialized(this.BuildManager);
ICollection<Type> controllerTypes = this.ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
switch (controllerTypes.Count)
{
:
return null;
:
return controllerTypes.First<Type>();
default:
throw DefaultControllerFactory.CreateAmbiguousControllerException(route, controllerName, controllerTypes);
}
}
/// <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();
}
}
internal IReadOnlyList<Type> GetControllerTypes()
{
this.ControllerTypeCache.EnsureInitialized(this.BuildManager);
return this.ControllerTypeCache.GetControllerTypes();
}
/// <summary>This API supports the ASP.NET MVC infrastructure and is not intended to be used directly from your code. This method calls the <see cref="M:System.Web.Mvc.DefaultControllerFactory.GetControllerSessionBehavior(System.Web.Routing.RequestContext,System.Type)" /> method.</summary>
/// <returns>The controller's session behavior.</returns>
/// <param name="requestContext">The request context.</param>
/// <param name="controllerName">The controller name.</param>
SessionStateBehavior IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (string.IsNullOrEmpty(controllerName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
Type controllerType = this.GetControllerType(requestContext, controllerName);
return this.GetControllerSessionBehavior(requestContext, controllerType);
}
}
既然是ControllerFactory,DefaultControllerFactory肯定也实现了IControllerFactory接口,别的我们就不看了,我们看看是如何创建Controller对象的,方法代码如下:
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);
}
代码很简单,该方法获取Controller的Type类型对象,然后根据Type对象创建实例。这个方法里面有两个辅助方法,一个是GetControllerType方法,另一个是GetControllerInstance方法,根据名称我们就能知道是做什么的。我们先看看GetControllerType方法的源码吧,这里是关键,没有Type对象的获取,以后都是空言:
protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (string.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch()))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
RouteData routeData = requestContext.RouteData;
if (routeData != null && routeData.HasDirectRouteMatch())
{
return DefaultControllerFactory.GetControllerTypeFromDirectRoute(routeData);
}
object obj;
if (routeData.DataTokens.TryGetValue("Namespaces", out obj))
{
IEnumerable<string> enumerable = obj as IEnumerable<string>;
if (enumerable != null && enumerable.Any<string>())
{
HashSet<string> namespaces = new HashSet<string>(enumerable, StringComparer.OrdinalIgnoreCase);
Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces);
if (controllerTypeWithinNamespaces != null || false.Equals(routeData.DataTokens["UseNamespaceFallback"]))
{
return controllerTypeWithinNamespaces;
}
}
}
)
{
HashSet<string> namespaces2 = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
Type controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaces2);
if (controllerTypeWithinNamespaces != null)
{
return controllerTypeWithinNamespaces;
}
}
return this.GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null);
}
我们先根据RouteData路由数据来获取Controller的类型对象,如果RouteData不为空,并且在RouteData的Values属性中包含Key为“MS_DirectRouteMatches”的值,那我们就据此获取Controller的类型对象,如果没找到就返回Null值,如果有一个值,就会作为Controller的Type类型值返回,如果多于一个就会抛出异常。如果RouteData不包含Key为“MS_DirectRouteMatches”的值,我们就根据RouteData对象中DataTokens属性Key为“Namespaces”来获取Controller的Type对象,同理,如果没找到就返回null,找到一个就直接返回,如果多余一个的话就抛出异常。
如果我们还是没找到怎么办呢?我们就要看看能不能使用后备命名空间,如果可以,就根据此命名空间来查找,我们从RouteData路由数据的DataTokens属性中查找是否包含有Key为“UseNamespaceFallback”的值,如果有,并且是False,就直接返回,结束查找,如果不包含Key为“UseNamepsaceFallback”的值或者该值为True,我们就可以根据ControllerBuilder的DefaultNamespaces属性表示后备命名空间查找Controller的类型,同理,没有找到就返回null,找到一个就作为结果值直接返回,如果多于一个那就要抛出异常了。
好了,大概的逻辑写完了,
三、扩展点
到了现在,Controller激活系统就写的差不多了,唯一还差一点的就是扩展点还没提。ASP.NET MVC号称几乎任何地方都可以扩展,Controller激活系统中肯定也包含着扩展点,下来我们一一详述。
四、小结
我们可以小结了,到此,Controler激活系统这个小节我就写完了。其实不是很复杂,大家在看的时候,刚开始别太关注代码的细节,先把握整个数据流向,或者叫请求脉络,把里面所涉及到的对象和关系都整理清楚了,然后再有针对性的去看涉及到的每个对象,不是画画图,不是画画,是画各个对象之间的关系图,图能画出来,说明你的心中就有了整体把握了。如果太关注细节,太关注某个类型的代码,也会很容易顾此失彼,因为我开始就是那样,总是看了后面忘记前面,请求的线路也不是很清楚。所以我们学习的时候也要有方法,方法对,学的就快,成就感就能很快产生。好了,说了这么多了,继续努力吧,希望我写的东西对大家有所帮助。
白话ASP.NET MVC之三:Controller是如何解析出来的的更多相关文章
- 白话ASP.NET MVC之二:Controller激活系统的概览
前文简介:我们抽象类路由规则的对象,RouteBase是路由对象的抽象基类,ASP.NET 的路由系统中有唯一一个从RouteBase继承的路由对象,那就是Route类型了.我们注册了路由对象Rout ...
- Asp.net MVC 中Controller返回值类型ActionResult
[Asp.net MVC中Controller返回值类型] 在mvc中所有的controller类都必须使用"Controller"后缀来命名并且对Action也有一定的要求: 必 ...
- 返璞归真 asp.net mvc (3) - Controller/Action
原文:返璞归真 asp.net mvc (3) - Controller/Action [索引页] [源码下载] 返璞归真 asp.net mvc (3) - Controller/Action 作者 ...
- ASP.NET MVC中controller和view相互传值的方式
ASP.NET MVC中Controller向view传值的方式: ViewBag.ViewData.TempData 单个值的传递 Json 匿名类型 ExpandoObject Cookie Vi ...
- ASP.NET MVC控制器Controller中参数
前述文章参见:ASP.NET MVC控制器Controller 绪论 之前的控制器返回的均为常量字符串,接下来展示如何获取请求传来的参数,而返回"动态"的字符串. 可以在操作方法B ...
- 【部分】ASP.NET MVC的Controller接收输入详解
原文:https://blog.csdn.net/lxrj2008/article/details/79455360 ASP.NET mvc的Controller要正确的响应用户发出的请求就要获取到用 ...
- 白话ASP.NET MVC之一:Url 路由
好久没有写关于ASP.NET MVC的东西了,虽然<ASP.NET MVC4框架揭秘>已经完完整整的看完一遍,但是感觉和一锅粥差不多,没什么可写的,因为我自己不理解,也就写不出来.现在开始 ...
- Asp.net MVC中 Controller 与 View之间的数据传递
在ASP.NET MVC中,经常会在Controller与View之间传递数据 1.Controller向View中传递数据 (1)使用ViewData["user"] (2)使用 ...
- 跟我学ASP.NET MVC之三:完整的ASP.NET MVC程序-PartyInvites
摘要: 在这篇文章中,我将在一个例子中实际地展示MVC. 场景 假设一个朋友决定举办一个新年晚会,她邀请我创建一个用来邀请朋友参加晚会的WEB程序.她提出了四个注意的需求: 一个首页展示这个晚会 一个 ...
随机推荐
- 【Android Developers Training】 72. 缩放一个视图
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- Spring MVC 项目搭建 -1- 创建项目
Spring MVC 项目搭建 -1- 创建项目 1.创建 Dynamic Web project (SpringDemo),添加相关jar包 2.创建一个简单的controller类 package ...
- PVM的安装和编译PVM程序
最近刚开始学习并发编程,学习到了PVM这一块.关于在linux系统中PVM的安装,真是要我的命,繁琐死了,最关键是我对linux也是刚开始学,还在继续学习<鸟哥的linux私房菜>一书.好 ...
- VB6之WebBrowser控件
UI短手或者GDI+用烦的童鞋可以借用WebBrowser打造漂亮的程序界面,只需要下载一个好看点的html代码就够了. 引用: Microsoft Html Object Library 部件: M ...
- javascript 玩转Date对象
前言:最近在做一个日期选择功能,在日期转换的时候经常换到晕,总结一下常用的Date对象的相关用法,方便日后直接查看使用- 1. new Date()的使用方法有: 不接收任何参数:返回当前时间: 接收 ...
- [CF373C]计算袋鼠是愉快的(Counting Kangaroos is Fun)-贪心
Problem 计算袋鼠是愉快的 题目大意 有n只袋鼠,如果一个袋鼠体积是另一个袋鼠的两倍或以上,则小袋鼠能被大袋鼠装进袋子里,装进去后就看不到袋子里的袋鼠了,问这群袋鼠如何行动才能使得它们看着数量最 ...
- Linux最小化安装
1,linux安装网络自动配置: 2,linux硬盘分配 1,/boot 用来存放与 Linux 系统启动有关的程序,比如启动引导装载程序等,建议大小为 100-200MB . 2,swap 实现虚拟 ...
- StringBuilder类中的重要方法
下面的API注解包含了StringBuilder类中的重要方法 append(boolean b):将 boolean 参数的字符串表示形式追加到序列. append(char c):将 char 参 ...
- 浅谈游戏中BOSS设计的思路
对于大多数游戏来说,BOSS在其设计上都有着不可替代的作用,也是玩家印象最为深刻的一部分.近期自己也有在做BOSS的设计工作,有一些心得想要分享一下: 1.明确BOSS的设计目的 在设计之初,我们一定 ...
- 【翻译】React vs Angular: JavaScript的双向性
翻译原文链接:https://blog.prototypr.io/react-vs-angular-two-sides-of-javascript-b850de22b413 我的翻译小站:http:/ ...