·您现在的位置: 云翼网络 >> 文章中心 >> 网站建设 >> 网站建设开发 >> ASP.NET网站开发 >> ASP.NET Web API的Controller是如何被创建的?

ASP.NET Web API的Controller是如何被创建的?

作者:佚名      ASP.NET网站开发编辑:admin      更新时间:2022-07-23

asp.net Web API的Controller是如何被创建的?

Web API调用请求的目标是定义在某个HttpController类型中的某个Action方法,所以消息处理管道最终需要激活目标HttpController对象。调用请求的URI会携带目标HttpController的名称,该名称经过路由解析之后会作为路由变量保存到一个HttPRouteData对象中,而后者会被添加到代表当前请求的HttpRequestMessage对象的属性字典中。ASP.NET Web API据此解析出目标HttpController的类型,进而实现针对目标HttpController实例的激活。[本文已经同步到《How ASP.NET Web API Works?》]

目录一、程序集的解析二、HttpController类型的解析 1、DefaultHttpControllerTypeResolver 2、HttpController类型的缓存三、HttpController的选择 1、DefaultHttpControllerSelector 2、获取目标HttpController的名称 3、建立HttpController名称与HttpControllerDescriptor之间的映射 4、根据请求选择HttpController四、HttpController的创建 1、HttpControllerActivator 2、DefaultHttpControllerActivator 3、DependencyResolver 4、HttpRequestMessage中的DependencyResolver 5、DependencyResolver在DefaultHttpControllerActivator中的应用一、程序集的解析在ASP.NET Web API的HttpController激活系统中,AssembliesResolver为目标HttpController类型解析提供候选的程序集。换句话说,候选HttpController类型的选择范围仅限于定义在由AssembliesResolver提供的程序集中的所有实现了IHttpController接口的类型。所有的AssembliesResolver均实现了接口IAssembliesResolver,该接口定义在命名空间“System.Web.Http.Dispatcher”下,如果未作特别说明,本节新引入的类型均定义在此命名空间下。如下面的代码片断所示,IAssembliesResolver接口中仅仅定义了一个唯一的GetAssemblies方法,该方法返回的正是提供的程序集列表。 1: public interface IAssembliesResolver 2: { 3: ICollection<Assembly> GetAssemblies(); 4: }默认使用的AssembliesResolver类型为DefaultAssembliesResolver。如下面的代码片断所示,DefaultAssembliesResolver在实现的GetAssemblies方法中直接返回当前应用程序域加载的所有程序集列表。 1: public class DefaultAssembliesResolver : IAssembliesResolver 2: { 3: public virtual ICollection<Assembly> GetAssemblies() 4: { 5: return AppDomain.CurrentDomain.GetAssemblies().ToList<Assembly>(); 6: } 7: }DefaultAssembliesResolver是默认使用的AssembliesResolver,那么默认的AssembliesResolver类型在ASP.NET Web API是如何确定的呢?要回答这个问题,需要涉及到另一个重要的类型ServicesContainer,它定义在命名空间“System.Web.Http.Controllers”下。由于DefaultAssembliesResolver在为HttpController类型解析提供的程序集仅限于当前应用程序域已经加载的程序集,如果目标HttpController定义在尚未加载的程序集中,我们不得不预先加载它们。但是这样的问题只会发生在Self Host寄宿模式下,如果采用Web Host寄宿模式则无此困扰,原因在于后者默认使用的是另一个AssembliesResolver类型。我们知道在Web Host寄宿模式下用于配置ASP.NET Web API消息处理管道的是通过类型GlobalConfiguration的静态只读属性Configuration返回的HttpConfiguration对象。从如下的代码片断我们可以发现,当GlobalConfiguration的Configuration属性被第一次访问的时候,在ServicesContainer中注册的AssembliesResolver会被替换成一个类型为WebHostAssembliesResolver的对象。 1: public static class GlobalConfiguration 2: { 3: //其他成员 4: static GlobalConfiguration() 5: { 6: _configuration = new Lazy<HttpConfiguration>(delegate 7: { 8: HttpConfiguration configuration = new HttpConfiguration( new HostedHttpRouteCollection(RouteTable.Routes)); 9: configuration.Services.Replace(typeof(IAssembliesResolver), new WebHostAssembliesResolver()); 10: //其他操作 11: return configuration; 12: }); 13: //其他操作 14: } 15: 16: public static HttpConfiguration Configuration 17: { 18: get 19: { 20: return _configuration.Value; 21: } 22: } 23: }WebHostAssembliesResolver是一个定义在程序集“System.Web.Http.WebHost.dll”中的内部类型。从如下的代码片断可以看出WebHostAssembliesResolver在实现的GetAssemblies方法中直接通过调用BuildManager的GetReferencedAssemblies方法来获取最终提供的程序集。 1: internal sealed class WebHostAssembliesResolver : IAssembliesResolver 2: { 3: ICollection<Assembly> IAssembliesResolver.GetAssemblies() 4: { 5: return BuildManager.GetReferencedAssemblies().OfType<Assembly>().ToList<Assembly>(); 6: } 7: }由于BuildManager的GetReferencedAssemblies方法几乎返回了在运行过程中需要的所有程序集,如果我们将HttpController类型定义在单独的程序集中,我们只要确保该程序集已经正常部属就可以了。如果有人对此感兴趣,可以试着将上面演示的实例从Self Host寄宿模式转换成Web Host寄宿模式,看看ASP.NET Web API的HttpController激活系统能否正常解析出分别定义在Foo.dll、Bar.dll和Baz.dll中的HttpController类型。二、HttpController类型的解析注册在当前ServicesContainer上的AssembliesResolver对象为HttpController类型的解析提供了可供选择的程序集,真正用于解析HttpController类型的是一个名为HttpControllerTypeResolver的对象。所有的HttpControllerTypeResolver类型均实现了接口IHttpControllerTypeResolver,如下面的代码片断所示,定义其中的唯一方法GetControllerTypes借助于提供的AssembliesResolver解析出所有的HttpController类型。 1: public interface IHttpControllerTypeResolver 2: { 3: ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver); 4: }与AssembliesResolver注册方式类似,默认使用的HttpControllerTypeResolver同样是注册在当前HttpConfiguration的ServicesContainer对象上。我们可以通过ServicesContainer具有如下定义的扩展方法GetHttpControllerTypeResolver得到这个注册的HttpControllerTypeResolver对象。 1: public static class ServicesExtensions 2: { 3: //其他成员 4: public static IHttpControllerTypeResolver GetHttpControllerTypeResolver(this ServicesContainer services); 5: }我们同样可以通过HttpConfiguration默认采用的DefaultServices的构造函数得到默认注册的HttpControllerTypeResolver对象的类型。如下面的代码片断所示,这个默认注册的HttpControllerTypeResolver是一个类型为DefaultHttpControllerTypeResolver的对象。 1: public class DefaultServices : ServicesContainer 2: { 3: //其他成员 4: public DefaultServices(HttpConfiguration configuration) 5: { 6: //其他操作 7: this.SetSingle<IHttpControllerTypeResolver>(new DefaultHttpControllerTypeResolver()); 8: } 9: }1、DefaultHttpControllerTypeResolver如下面的代码片断所示, DefaultHttpControllerTypeResolver具有一个Predicate<Type>类型的只读属性IsControllerTypePredicate,返回的委托对象用于判断指定的类型是否是一个有效的HttpController类型。 1: public class DefaultHttpControllerTypeResolver : IHttpControllerTypeResolver 2: { 3: public DefaultHttpControllerTypeResolver(); 4: public DefaultHttpControllerTypeResolver(Predicate<Type> predicate); 5: 6: public virtual ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver); 7: 8: protected Predicate<Type> IsControllerTypePredicate { get; } 9: }如果我们具有特别的HttpController类型有效性验证规则,可以在调用构造函数实例化DefaultHttpControllerTypeResolver对象时通过参数指定这个Predicate<Type>委托对象。在默认情况下,这个自动初始化的Predicate<Type>对象体现了默认采用的HttpController类型有效验证规则。具体来说,默认情况下一个给定的类型必须同时满足如下的条件才是一个有效的HttpController类型。是一个外部可见(IsVisible = true)的实例(IsAbstract = false)类(IsClass = true)。类型直接或者间接实现了接口IHttpController。类型名称必须以“Controller”为后缀,但是不区分大小写(可以使用“controller”作为后缀)。用于提供所有有效HttpController类型的GetControllerTypes方法的实现逻辑其实很简单。它通过指定的AssembliesResolver得到一个程序集列表,对于定义在这些程序集中的所有类型,如果满足上述的要求就是返回的HttpController类型之一。定义在类型DefaultHttpControllerTypeResolver中的针对有效HttpController类型的解析逻辑基本上体现在如下所示的代码中。 1: public class DefaultHttpControllerTypeResolver : IHttpControllerTypeResolver 2: { 3: //其他成员 4: public virtual ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver) 5: { 6: List<Type> types = new List<Type>(); 7: foreach (Assembly assembly in assembliesResolver.GetAssemblies()) 8: { 9: foreach (Type type in assembly.GetTypes()) 10: { 11: if (this.IsControllerTypePredicate(type)) 12: { 13: types.Add(type); 14: } 15: } 16: } 17: return types; 18: } 19: }2、HttpController类型的缓存由于针对所有HttpController类型的解析需要大量使用到反射,这是一个相对耗时的过程,所以ASP.NET Web API会对解析出来的HttpController类型进行缓存。具体的缓存实现在具有如下定义的HttpControllerTypeCache类型中,这是一个定义在程序集“System.Web.Http.dll”中的内部类型。 1: internal sealed class HttpControllerTypeCache 2: { 3: //其他成员 4: internal Dictionary<string, ILookup<string, Type>> Cache { get; } 5: }缓存的HttpController类型通过只读属性Cache获取,这是一个类型为Dictionary<string, ILookup<string, Type>>的字典对象。该字典的Key表示HttpController的名称(HttpController类型名称去除“Controller”后缀),其Value返回的ILookup<string, Type>对象包含一组具有相同名称的HttpController类型列表,自身的Key表示HttpController类型的命名空间。三、目标HttpController的选择AssembliesResolver仅仅是将所有合法的HttpController类型解析出来,针对具体的调用请求,系统必须从中选择一个与当前请求匹配的HttpController类型出来。HttpController的选择通过HttpControllerSelector对象来完成,所有的HttpControllerSelector类型均实现了具有如下定义的接口IHttpControllerSelector。 1: public interface IHttpControllerSelector 2: { 3: IDictionary<string, HttpControllerDescriptor> GetControllerMapping(); 4: HttpControllerDescriptor SelectController(HttpRequestMessage request); 5: }如上面的代码片断所示,该接口中定义了GetControllerMapping和SelectController两个方法。GetControllerMapping返回一个描述所有HttpController类型的HttpControllerDescriptor对象与对应的HttpController名称之间的映射关系。针对请求的HttpController选择实现在SelectController方法中,它返回描述目标HttpController的HttpControllerDescriptor对象。1、DefaultHttpControllerSelector默认使用HttpControllerSelector依然注册到当前的ServicesContainer对象中,我们可以调用ServicesContainer如下所示的扩展方法GetHttpControllerSelector得到注册的HttpCont