通過《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的釋放來完成。
文章列表