文章出處

通過《ASP.NET Web API的Controller是如何被創建的?》我們已經對HttpController激活系統的核心對象有了深刻的了解,這些對象包括用于解析程序集和有效HttpController類型的AssembliesResolver和HttpControllerTypeResolver、根據請求完整目標HttpController選擇的HttpControllerSelector、負責激活目標HttpController實例的HttpControllerActivator、以及作為IoC容器的DependencyResolver。[本文已經同步到《How ASP.NET Web API Works?》]

一、HttpController激活流程

對于組成ASP.NET Web API核心框架的消息處理管道來說,處于末端的HttpMessageHandler是一個HttpRoutingDispatcher對象。當它完成路由解析工作之后(HttpRoutingDispatcher的路由解析只發生在Self Host寄宿模式下,對于Web Host寄宿模式來說,路由解析工作是由ASP.NET路由系統來完成的),在默認情況下它會將請求傳遞給一個HttpControllerDispatcher對象。

HttpControllerDispatcher實現了目標HttpController對象的激活與執行,并將代表執行結果的HttpResponseMessage對象返回給HttpRoutingDispatcher對象,后者將此HttpResponseMessage回傳給消息管道進行相應處理后最終完成對請求的響應。

右圖所示的UML體現了激活HttpController的整個流程。當HttpControllerDispatcher接管請求之后,它會獲取注冊的HttpControllerSelector對象,并調用其SelectController方法得到描述目標HttpController的HttpControllerDescriptor對象。

默認注冊的HttpControllerSelector是一個DefaultHttpControllerSelector對象,后者借助于注冊的HttpControllerTypeResolver對象得到所有HttpController類型,進而創建一個描述這些HttpController的HttpControllerDescriptor對象與HttpController名稱之間的映射關系。當它的SelectController方法被執行后,它只需要根據請求攜帶的HttpRouteData對象獲取目標HttpController的名稱,并從此映射關系中選擇對應的HttpControllerDescriptor即可。

HttpControllerDispatcher接下來調用這個HttpControllerDescriptor對象的CreateController方法得到激活的HttpController對象。對于這個HttpControllerDescriptor對象來說,當它的CreateController方法被調用之后,它會獲取注冊的HttpControllerActivator對象,并調用其Create方法實現針對目標HttpController對象的激活并將激活的對象返回。

默認注冊的DefaultHttpControllerActivator對象會利用注冊的DependencyResolver根據HttpController類型去獲取代表目標HttpController實例的對象。如果后者返回一個具體的HttpController對象,該對象將直接作為方法的返回值,否則DefaultHttpControllerActivator直接采用反射的形式創建目標HttpController對象并返回。

由于默認注冊的DependencyResolver是一個EmptyResolver對象,由它返回的HttpController對象總是Null,所以在默認情況下激活的HttpController對象總是以反射的形式創建的。正因為如此,我們定義的HttpController類型必須具有一個默認構造函數。

二、HttpController的釋放

我們知道作為自定義HttpController默認基類的ApiController類型實現了IDispoable接口,資源釋放工作可以通過調用實現的Dispose方法來完成,那么這個方法是在什么時候執行的呢?除此之外,我們知道表示請求的HttpRequestMessage類型具有一個字典類型的屬性Properties,我們可以利用它將任何一個對象附加到一個HttpRequestMessage對象上,如果這些附加對象需要實施資源釋放操作,這些操作又是在什么時候被執行的呢

實際上HttpRequestMessage通過Properties屬性表示的屬性字典為需要釋放的資源預留了存儲空間,對應的Key為“MS_DisposableRequestResources”,對應的值是一個List<IDisposable>的對象。我們可以調用HttpRequestMessage具有如下定義的擴展方法GetResourcesForDisposal得到這個列表,也可以調用擴展方法RegisterForDispose將一個或者多個類型實現了IDisposable接口的對象放到這個列表中。

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

ASP.NET Web API還為釋放這些附加到HttpRequestMessage上的對象定義了如上一個擴展方法DisposeRequestResources,那么這個方法究竟是在什么時候被調用的呢?

釋放這些資源的時機取決于采用的寄宿模式。對于Web Host來說,ASP.NET Web API用于“處理請求、回復響應”的HttpMessageHandler管道是由HttpControllerHandler創建的,后者根據當前HTTP上下文創建一個表示當前請求的HttpRequestMessage對象并傳入這個管道進行處理。在整個管道完成對請求的處理并最終對請求予以響應之后,HttpControllerHandler會負責完成如下三項與資源釋放有關的工作。

  • 調用HttpRequestMessage對象的擴展方法DisposeRequestResources釋放附加在自身屬性字典中的對象。
  • 調用HttpRequestMessage對象的Dispose方法對請求消息本身作相應的釋放工作。
  • 調用返回的HttpResponseMessage對象對響應消息作相應的釋放工作。

對于Self Host來說,通過《Self Host模式下的ASP. NET Web API是如何進行請求的監聽與處理的?》的介紹我們知道請求的監聽、接收和響應是通過HttpBinding創建的信道棧來完成的。該信道棧處理的消息類型為HttpMessage,具體代表請求消息和響應消息的HttpMessage分別是對HttpRequestMessage和HttpResponseMessage對象的封裝。WCF中表示消息的Message本身就是一個需要最終被釋放的對象,在針對它的處理結束之后會調用其Close或者Dispose方法對它進行資源釋放的工作。

當一個HttpMessage對象的Close或者Dispose方法被調用的時侯,被其封裝的HttpRequestMessage或者HttpResponseMessage會相應地得到釋放。對于請求消息來說,具體的資源釋放工作包括針對HttpRequestMessage自身的釋放和對附加到屬性字典中資源的釋放。

我們不妨通過一個簡單的實例來演示Self Host寄宿模式下伴隨著HttpMessage對象的釋放對被封裝的HttpRequestMessage對象的釋放。我們在一個控制臺應用中定義了如下三個需要被釋放的類型Foo、Bar和Baz,它們共同的基類DisposableObject實現了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: {}
  11:  
  12: public class Bar : DisposableObject
  13: {}
  14:  
  15: public class Baz : DisposableObject
  16: {}

然后我們再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對象上的資源能夠在它釋放的時候得到釋放。(S406)

   1: Foo.Dispose()
   2: Bar.Dispose()
   3: Baz.Dispose()

對于ApiController來說,當它的ExecuteAsync方法被執行的時候,它會調用擴展方法RegisterForDispose將自己注冊到代表當前請求的HttpRequestMessage對象上。毫無疑問,ApiController對象的釋放會通過對這個HttpRequestMessage的釋放來完成。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()