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

通过上面的介绍我们知道利用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()
作者:Artech
出处:http://artech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的创建的更多相关文章

  1. 目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的选择

    目标HttpController在ASP.NET Web API中是如何被激活的:目标HttpController的选择 ASP.NET Web API能够根据请求激活目标HttpController ...

  2. 剖析Asp.Net Web API中HttpController的激活

    在Asp.Net Web API中,请求的目标是定义在某个HttpController中的某个Action方法.当请求经过Asp.Net Web API消息处理管道到达管道"龙尾" ...

  3. ASP.NET Web API中的Controller

    虽然通过Visual Studio向导在ASP.NET Web API项目中创建的 Controller类型默认派生与抽象类型ApiController,但是ASP.NET Web API框架本身只要 ...

  4. 在 ASP.NET Web API 中使用 Attribute 统一处理异常

    并非所有的异常都需要 try-catch 进行重复的处理,这会导致大量的重复性代码,一旦后续系统出现异常处理机制的修改,随着代码量增多,修改也会变的更加困难. ASP.NET Web API 中特别增 ...

  5. 在ASP.NET Web API中使用OData

    http://www.alixixi.com/program/a/2015063094986.shtml 一.什么是ODataOData是一个开放的数据协议(Open Data Protocol)在A ...

  6. ASP.NET Web API 中的异常处理(转载)

    转载地址:ASP.NET Web API 中的异常处理

  7. 【ASP.NET Web API教程】6.2 ASP.NET Web API中的JSON和XML序列化

    谨以此文感谢关注此系列文章的园友!前段时间本以为此系列文章已没多少人关注,而不打算继续下去了.因为文章贴出来之后,看的人似乎不多,也很少有人对这些文章发表评论,而且几乎无人给予“推荐”.但前几天有人询 ...

  8. Asp.Net Web API 2第十三课——ASP.NET Web API中的JSON和XML序列化

    前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs.com/aehyok/p/3446289.html 本文描述ASP.NET W ...

  9. ASP.NET WEB API 中的路由调试与执行过程跟踪

    路由调试 RouteDebugger 是调试 ASP.NET MVC 路由的一个好的工具,在ASP.NET WEB API中相应的有 WebApiRouteDebugger ,Nuget安装 Inst ...

随机推荐

  1. Postman 是一个非常棒的Chrome扩展,提供功能强大的API & HTTP 请求调试

    Postman 是一个非常棒的Chrome扩展,提供功能强大的API & HTTP 请求调试   需要FQ才能安装,使用时应该不用FQ了,除非使用postman的历史记录功能:   非常棒的C ...

  2. 使用myeclipse创建带注解的model实体类

    1.先新建JPA项目: 如果没有就点击左下角的Show All Wizards. 点两次Next后,点击Finish即可,中间不用任何操作 (点第二次Next后会出现连接到所在数据库,先不管)     ...

  3. GitBook配置

    GitBook 是一个通过 Git 和 Markdown 来撰写书籍的工具.生成格式有:JSON.ePub.PDF.Website ! ================================ ...

  4. Swift语言指南(七)--语言基础之布尔值和类型别名

    原文:Swift语言指南(七)--语言基础之布尔值和类型别名 布尔值 Swift有一个基本布尔类型,叫做布尔(bool),布尔值又称逻辑值(logical),因为它只能为真(true)或假(false ...

  5. app后端设计(0)--总文件夹

    原文:http://blog.csdn.net/newjueqi/article/details/19003775 做了接近两年app相关的系统架构,api设计,先后在两个创业公司中工作,经历过手机网 ...

  6. 【Unity 3D】学习笔记四十一:关节

    关节 关节组件能够加入至多个游戏对象中,而加入关节的游戏对象将通过关节连接在一起而且感觉连带的物理效果.须要注意的是:关节必须依赖于刚体组件. 关节介绍 关节一共分为5大类:链条关节,固定关节,弹簧关 ...

  7. iOS--Swift开发中的单例设计模式

    最近在开发一个小的应用,遇到了一些Objective-c上面常用的单例模式,但是swift上面还是有一定区别的,反复倒来倒去发现不能按常理(正常的oc to swift的方式)出牌,因此搜索了一些帖子 ...

  8. 认识TDD

    初步认识TDD TDD,测试驱动开发(Test Driven Development)是极限编程中倡导的程序开发方法,以其倡导先写测试程序,然后编码实现其功能得名.本文将对TDD有一个较为系统的认识. ...

  9. C#正则学习

    正则的力量无法小觑,短短的几个字符,往往胜过几十行的代码,大大可以简化我们冗余的代码. 以前在js里用正则比较多,今天来熟悉下C#中正则的使用方法,权当笔记了! 如果把正则当做一门语言的话,那么正则的 ...

  10. A在SP.NET跨页多选

    在ASP.NET跨页多选 本文介绍怎样在ASP.NET中实现多页面选择的问题.其详细思路非常easy:用隐藏的INPUT记住每次选择的项目,在进行数据绑定时.检查保存的值,再在DataGrid中进行选 ...