目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的创建_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的创建

目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的创建

 2013/8/27 9:59:25  Artech  博客园  我要评论(0)
  • 摘要:通过上面的介绍我们知道利用HttpControllerSelector可以根据表示当前请求的HttpRequestMessage得到描述目标HttpController的HttpControllerDescriptor对象。在前面介绍HttpControllerDescriptor的时候我们提到过:HttpControllerDescriptor自身具有创建对应HttpController的能力,具体体现在它的CreateController方法上
  • 标签:.net ASP.NET API 创建 Web net HTTP controller

通过上面的介绍我们知道利用HttpControllerSelector可以根据表示当前请求的HttpRequestMessage得到描述目标HttpController的HttpControllerDescriptor对象。在前面介绍HttpControllerDescriptor的时候我们提到过:HttpControllerDescriptor自身具有创建对应HttpController的能力,具体体现在它的CreateController方法上。接下来我们就来着重介绍实现在这个CreateController方法中HttpController创建机制。[本文已经同步到《How ASP.NET Web API Works?》]

目录
HttpControllerActivator
DefaultHttpControllerActivator
DependencyResolver
HttpRequestMessage中的DependencyResolver
DependencyResolver在DefaultHttpControllerActivator中的应用
DependencyScope的释放
实例演示:伴随着HttpMessage的关闭对资源的释放

HttpControllerActivator

针对请求对目标HttpController的激活机制最终落实到一个名为HttpControllerActivator的对象上,所有的HttpControllerActivator均实现了IHttpControllerActivator接口。如下面的代码片断所示,IHttpControllerActivator具有唯一的方法Create方法根据指定的表示请求的HttpRequestMessage对象、描述激活HttpController的HttpControllerDescriptor以及目标HttpController的类型来创建对应的HttpController。

   1: public interface IHttpControllerActivator
   2: {
   3:     IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType);
   4: }

忠实的读者朋友们一定知道像这样的“标准化组件”自然也是注册到当前HttpConfiguration的ServicesContainer上被HttpController激活系统使用的。我们可以通过ServicesContainer具有如下定义的扩展方法GetHttpControllerActivator直接获取注册的HttpControllerActivator对象。

   1: public static class ServicesExtensions
   2: {
   3:     //其他成员
   4:     public static IHttpControllerActivator GetHttpControllerActivator(this ServicesContainer services);
   5: }

实际上HttpControllerDescriptor的CreateController方法就是调用上述这个扩展方法得到 注册的HttpControllerActivator对象,并调用它的Create方法来创建目标HttpController的。如下的代码体现CreateController方法真正的实现逻辑。

   1: public class HttpControllerDescriptor
   2: {
   3:     //其他成员
   4:     public virtual IHttpController CreateController(HttpRequestMessage request)
   5:     {        
   6:         return this.Configuration.Services.GetHttpControllerActivator().Create(request, this, this.ControllerType);
   7:     }
   8: }

DefaultHttpControllerActivator

我们照例利用通过DefaultServices的构造函数定义分析出默认注册的HttpControllerActivator是个怎样的对象。如下面的代码片断所示,当DefaultServices被创建的时候会创建并注册一个类型为DefaultHttpControllerActivator对象。

   1: public class DefaultServices : ServicesContainer
   2: {
   3:     //其他成员
   4:     public DefaultServices(HttpConfiguration configuration)
   5:     {
   6:         //其他操作
   7:         this.SetSingle<IHttpControllerActivator>(new DefaultHttpControllerActivator ());
   8:     }
   9: }

接下来我们就来分析下在DefaultHttpControllerActivator类型的Create方法中是如何激活,不过要真正了解实现在DefaultHttpControllerActivator的HttpController激活机制,我们需要认识另一个名为DependencyResolver的对象。

DependencyResolver

说到DependencyResolver,我们又不得不谈到IoC的概念。我们知道IoC常和另一个术语“依赖注入(DI,Dependency Injection)”联系在一起。通过IoC容器激活的对象可能具有其他对象的依赖,而且被依赖的对象可能具有针对另一个对象的依赖,所以IoC容器需要在提供所需对象之前已经帮助我们解决了这些依赖。关于IoC以及它在HttpController激活过程中的应用,我们会在下一节中进行单独介绍。

从命名也可以看出来,这里介绍DependencyResolver与依赖注入有关,我们可以将它视为ASP.NET Web API内部使用的IoC容器。所有的DependencyResolver实现了具有如下定义的接口IDependencyResolver,这个接口的定义有点特别:它具有唯一个返回类型为IDependencyScope的BeginScope方法,IDependencyResolver接口本身同时也继承IDependencyScope这个接口,并且这两个接口又都继承自IDisposable接口。

   1: public interface IDependencyResolver : IDependencyScope, IDisposable
   2: {
   3:     IDependencyScope BeginScope();
   4: }
   5:  
   6: public interface IDependencyScope : IDisposable
   7: {
   8:     object GetService(Type serviceType);
   9:     IEnumerable<object> GetServices(Type serviceType);
  10: }

通过DependencyResolver的BeginScope方法创建的IDependencyScope类型的对象可以视为一个用于激活对象的上下文,我们可以通过调用它的GetService和GerServices方法根据指定的“服务接口类型”获取对应的服务实例。由于IDependencyScope继承自IDisposable,所以与此上下文关联的资源释放工作可以通过实现的Dispose方法来完成。

与上面我们介绍的那些“标准化组件”不同,默认使用的DependencyResolver并未注册到当前HttpConfiguration的ServicesContainers上,因为它直接注册到当前HttpConfiguration上面。如下面的代码片断所示,当前使用的DependencyResolver直接通过HttpConfiguration的DependencyResolver属性或者和设置。

   1: public class HttpConfiguration : IDisposable
   2: {
   3:     //其他成员    
   4:     public HttpConfiguration(HttpRouteCollection routes)
   5:     {       
   6:         this._dependencyResolver = EmptyResolver.Instance ;
   7:     }   
   8:  
   9:     public IDependencyResolver DependencyResolver
  10:     {
  11:         get { return this._dependencyResolver; }
  12:         set { this._dependencyResolver = value; }
  13:     }
  14: }

从上面的代码片断我们还可以看出默认注册到HttpConfiguration上的DependencyResolver是通过类型EmptyResolver的静态属性Instance返回的EmptyResolver对象。EmptyResolver是一个定义在程序集System.Web.Http.dll中的内部类型,其成员定义如下。之所以将它如此命名,原因在于它仅仅是一个“空”的IoC容器:它的BeginScope返回的是它自身,GetService和GetServices方法分别返回Null和一个空对象集合,而Dispose方法也没有任何资源释放工作要做。

   1: internal class EmptyResolver : IDependencyResolver, IDependencyScope, IDisposable
   2: {
   3:     public IDependencyScope BeginScope();
   4:     public void Dispose();
   5:     public object GetService(Type serviceType);
   6:     public IEnumerable<object> GetServices(Type serviceType);
   7:     public static IDependencyResolver Instance { get; }
   8: }

HttpRequestMessage中的DependencyResolver

虽然当前使用的DependencyResolver是注册到当前HttpConfiguration上的,但是我们可以直接从表示当前请求的HttpRequestMessage对象中获取道通过它创建的DependencyScope对象。如下面的代码片断所示,HttpRequestMessage具有一个返回类型为IDependencyScope的GetDependencyScope方法。

   1: public static class HttpRequestMessageExtensions
   2: {
   3:     //其他成员    
   4:     public static IDependencyScope GetDependencyScope(this HttpRequestMessage request);
   5: }

其实这个扩展方法实现逻辑很简单:HttpRequestMessage的属性字典为放置DependencyScope对象预定了一个Key,其值为“MS_DependencyScope”,对应着HttpPropertyKeys具有如下定义的静态只读字段DependencyScope。

   1: public static class HttpPropertyKeys
   2: {
   3:     //其他成员
   4:     public static readonly string DependencyScope;
   5: }

如果根据这个Key不能从HttpRequestMessage的属性字典中找到一个DependencyScope对象,则会通过当前的HttpConfiguration(实际上当前的HttpConfiguration也已经被HttpServer添加到了这个HttpRequestMessage中了)得到注册的DependencyResolver,然后利用它创建一个DependencyScope对象并添加到HttpRequestMessage的属性字典中,那么后续过程如果需要使用到此DependencyScope就可以直接从HttpRequestMessage中提取了。

DependencyResolver在DefaultHttpControllerActivator中的应用

在了解了DependencyResolver是怎样一个对象之后,我们再回头讨论实现在DefaultHttpControllerActivator的Create方法中针对请求的HttpController激活机制。其实实现机制非常简单:DefaultHttpControllerActivator先通过调用表示当前请求的HttpRequestMessage的扩展方法GetDependencyScope得到通过当前DependencyResolver创建的DependencyScope对象,然后将目标HttpController的类型作为参数调用其GetService方法

如果该方法返回一个具体的HttpController对象,该对象就是Create方法的返回值,否则直接直接根据目标HttpController的类型进行反射创建一个HttpController对象并返回。如下所示的代码片断基本上体现了DefaultHttpControllerActivator的HttpController激活机制。

   1: public class DefaultHttpControllerActivator : IHttpControllerActivator
   2: {
   3:     public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
   4:     {
   5:         IDependencyScope depedencyScope = request.GetDependencyScope();
   6:         object httpController = depedencyScope.GetService(controllerType)?? Activator.CreateInstance(controllerType);
   7:         return httpController as IHttpController;
   8:     }
   9: }

由于默认请求下注册到当前HttpConfiguration上的DependencyResolver是一个EmptyResolver对象,而它的GetService方法总是返回Null,所以默认情况下对HttpController的激活最终是对目标HttpController类型的反射实现的。

关于HttpController的激活,我还想强调一点:在默认情况下,解析出来的所有HttpController类型会被缓存,创建的用于描述HttpControllerDescriptor也会被缓存,但是ASP.NET Web API的HttpController激活系统并不会对创建的HttpController实施缓存。换言之,不管多个请求是否针对相同的HttpController类型,被激活的HttpController实例都是不同的。

DependencyScope的释放

这是一个很少人会意识到的问题:被添加到表示当前请求的HttpRequestMessage中用于根据类型激活相应实例(不仅限于HttpController对象)的DependencyScope类型实现了IDisposable接口,那么用于释放该DependencyScope的Dispose方法在什么时候被调用呢?

在HttpRequestMessage的属性字典中为需要释放的资源预留的存储空间,对应的Key为“MS_DisposableRequestResources”,HttpPropertyKeys具有如下定义的静态只读字段DisposableRequestResourcesKey返回这个Key。

   1: public static class HttpPropertyKeys
   2: {
   3:     //其他成员
   4:     public static readonly string DisposableRequestResourcesKey;
   5: }

这个消息属性保存的是一个类型为List<IDisposable>的对象。我们可以调用HttpRequestMessage具有如下定义的扩展方法GetResourcesForDisposal得到这个列表,也可以调用扩展方法RegisterForDispose和RegisterForDispose将一个或者多个类型实现了IDisposable接口的对象放到这个列表中。在ASP.NET Web API的应用编程接口中还为释放这些附加到HttpRequestMessage上的对象定义了如下一个扩展方法DisposeRequestResources,那么这个方法究竟是在什么时候被调用的呢?

   1: public static class HttpRequestMessageExtensions
   2: {
   3:     //其他成员
   4:     public static IEnumerable<IDisposable> GetResourcesForDisposal(this HttpRequestMessage request);
   5:     public static void RegisterForDispose(this HttpRequestMessage request, IEnumerable<IDisposable> resources);
   6:     public static void RegisterForDispose(this HttpRequestMessage request, IDisposable resource);
   7:     public static void DisposeRequestResources(this HttpRequestMessage request);
   8: }

具体释放这些资源的时机取决于采用的寄宿模式。对于Web Host来说,通过“ASP.NET Web API的消息处理管道: HttpRoutingDispatcher”的介绍我们知道ASP.NET Web API用于“处理请求、回复响应”的HttpMessageHandler管道是由HttpControllerHandler创建的,后者根据当前HTTP上下文创建一个表示当前请求的HttpRequestMessage对象并传入这个管道进行处理。在整个管道完成对请求的请求并最终对请求予以响应之后,HttpControllerHandler会负责完成如下三项与资源释放有关的工作:

  • 调用HttpRequestMessage对象的扩展方法DisposeRequestResources释放附加在自身属性字典中的对象。
  • 调用HttpRequestMessage对象的Dispose方法对请求消息本身作相应的释放工作。
  • 调用返回的HttpResponseMessage对象对响应消息作相应的释放工作。

对于Self Host来说,通过《Self Host下的消息处理管道》的介绍我们知道请求的监听、接收和响应是通过HttpBinding创建的信道栈来完成的。该信道栈处理的消息类型为HttpMessage,具体代表请求消息和响应消息的HttpMessage分别是对HttpRequestMessage和HttpResponseMessage对象的封装。WCF中表示消息的Message本身就是一个需要最终被释放的对象,在针对它的处理结束之后会调用其Close或者Dispose对它进行资源释放的工作。

当一个HttpMessage对象的Close或者Dispose方法被调用的时侯,被其封装的HttpRequestMessage或者HttpResponseMessage会相应地得到释放。对应请求消息来说,具体的资源释放工作包括针对HttpRequestMessage自身的释放和对附加到属性字典中资源的释放。

实例演示:伴随着HttpMessage的关闭对资源的释放

我们不妨通过一个简单的实例来演示Self Host寄宿模式下伴随着HttpMessage对象的释放对被封装的HttpRequestMessage释放。我们在一个控制台应用中定义了如下三个需要被释放的类型Foo、Bar和Baz,它们共同的基类DispsoableObject实现了IDisposable接口,并在实现Dispose方法中通过输出一段文字以确定具体的释放操作是否被执行。

   1: public class DisposableObject : IDisposable
   2: {
   3:     public void Dispose()
   4:     {
   5:         Console.WriteLine("{0}.Dispose()", this.GetType().Name);
   6:     }
   7: }
   8:  
   9: public class Foo : DisposableObject{}
  10: public class Bar : DisposableObject{}
  11: public class Baz : DisposableObject{}

然后我们再Main方法中编写了如下一段简短的程序。我们分别创建了类型为Foo、Bar和Baz的三个对象,并通过调用扩展方法RegisterForDispose将它们注册到创建的HttpRequestMessage对象上。我们针对这个HttpRequestMessage对象利用反射的方式创建了一个HttpMessage对象,最终调用其Close方法对它作相应的释放工作。

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         HttpRequestMessage request = new HttpRequestMessage();
   6:         request.RegisterForDispose(new Foo());
   7:         request.RegisterForDispose(new Bar());
   8:         request.RegisterForDispose(new Baz());
   9:  
  10:         Type httpMessageType = Type.GetType("System.Web.Http.SelfHost.Channels.HttpMessage, System.Web.Http.SelfHost");
  11:         Message httpMessage = (Message)Activator.CreateInstance(httpMessageType, new object[] { request });
  12:         httpMessage.Close();
  13:     }
  14: }

运行这段程序后会控制台上将会产生如下的输出结果,由此可见通过调用扩展方法RegisterForDispose注册到某个HttpRequestMessage对象上的资源能够在它释放的时候得到释放。(S405)

   1: Foo.Dispose()
   2: Bar.Dispose()
   3: Baz.Dispose()
发表评论
用户名: 匿名