雖然通過Visual Studio向?qū)г贏SP.NET Web API項目中創(chuàng)建的 Controller類型默認(rèn)派生與抽象類型ApiController,但是ASP.NET Web API框架本身只要求它實現(xiàn)IHttpController接口即可,所以我們將其統(tǒng)稱為HttpController。既然HttpController指的是所有實現(xiàn)了IHttpController接口的類型,我們自然得先來了解一下這個接口的定義。如下面的代碼片斷所示,在IHttpController接口中僅僅定義了唯一的方法ExecuteAsync方法,它以異步的方式執(zhí)行HttpController,并返回一個Task<HttPResponseMessage>對象。[本文已經(jīng)同步到《How ASP.NET Web API Works?》]
1: public interface IHttpController
2: {
3: Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken);
4: }
HttpController可以視為對ASP.NET Web API的消息處理管道的延續(xù)。通過“ASP.NET Web API標(biāo)準(zhǔn)的“管道式”設(shè)計”的介紹我們知道位于管道末端的是一個HttpRoutingDispatcher對象。當(dāng)SendAsync方法被執(zhí)行的時候,HttpRoutingDispatcher會利用隸屬于它的HttpControllerDispatcher來激活目標(biāo)HttpController對象,隨后調(diào)用該對象的ExecuteAsync方法并將返回的Task<HttpResponseMessage>對象作為返回值。右圖揭示了包含激活的HttpController在內(nèi)的消息處理管道的結(jié)構(gòu)。
與HttpMessageHandler的SendAsync方法有所不同,HttpController的ExecuteAsync方法并沒有一個表示請求的類型為HttpRequestMessage的參數(shù),取而代之的是一個HttpControllerContext類型的參數(shù)。HttpControllerContext定義在命名空間“System.Web.Http.Controllers”下,表示執(zhí)行HttpController的上下文。
如下面的代碼片斷所示,通過定義在HttpControllerContext中的屬性我們可以得到用于配置消息處理管道的HttpConfiguration對象和封裝路由數(shù)據(jù)的HttpRouteData對象,以及表示當(dāng)前請求的HttpRequestMessage對象。這三個屬性可以在構(gòu)建HttpControllerContext的時候直接通過構(gòu)造函數(shù)的參數(shù)指定,我們也可以先創(chuàng)建一個空的HttpControllerContext對象之后直接對這些屬性賦值。
1: public class HttpControllerContext
2: {
3: public HttpControllerContext();
4: public HttpControllerContext(HttpConfiguration configuration, IHttpRouteData routeData, HttpRequestMessage request);
5:
6: public HttpConfiguration Configuration { get; set; }
7: public IHttpRouteData RouteData { get; set; }
8: public HttpRequestMessage Request { get; set; }
9:
10: public IHttpController Controller { get; set; }
11: public HttpControllerDescriptor ControllerDescriptor { get; set; }
12: }
一個HttpControllerContext對象表示執(zhí)行HttpController的上下文,我們可以通過Controller屬性來獲取或者設(shè)置這個HttpController對象。除此之外,我們還可以利用另一個屬性ControllerDescriptor獲取或者設(shè)置用于描述HttpController的HttpControllerDescriptor對象(類型HttpControllerDescriptor定義在命名空間“System.Web.Http.Controllers”下)。
HttpControllerDescriptor封裝了某個HttpController類型的元數(shù)據(jù),我們可以將它視為某個HttpController類型的描述對象。HttpControllerDescriptor具有根據(jù)元數(shù)據(jù)創(chuàng)建對應(yīng)HttpController的能力,實際上ASP.NET Web API的HttpController激活系統(tǒng)就是根據(jù)HttpControllerDescriptor來創(chuàng)建目標(biāo)HttpController的。
如下面的代碼片斷所示,我們可以通過HttpControllerDescriptor的屬性Configuration、ControllerName和ControllerType獲取當(dāng)前的HttpConfiguration對象和被描述HttpController的名稱和類型。這三個屬性可以在構(gòu)建HttpControllerDescriptor時通過構(gòu)造函數(shù)的參數(shù)顯式指定,也可以先構(gòu)建一個空的HttpControllerDescriptor對象,然后手工設(shè)置這些屬性。
1: public class HttpControllerDescriptor
2: {
3: public HttpControllerDescriptor();
4: public HttpControllerDescriptor(HttpConfiguration configuration, string controllerName, Type controllerType);
5:
6: public virtual IHttpController CreateController(HttpRequestMessage request);
7:
8: public virtual Collection<T> GetCustomAttributes<T>() where T: class;
9: public virtual Collection<T> GetCustomAttributes<T>(bool inherit) where T: class;
10: public virtual Collection<IFilter> GetFilters();
11:
12: public HttpConfiguration Configuration { get; set; }
13: public string ControllerName { get; set; }
14: public Type ControllerType { get; set; }
15:
16: public virtual ConcurrentDictionary<object, object> Properties { get; }
17: }
HttpControllerDescriptor具有創(chuàng)建HttpController的能力主要體現(xiàn)在其CreateController方法上,該方法完成了目標(biāo)方法的激活。換句話說,目標(biāo)HttpController的激活是通過調(diào)用描述它的HttpControllerDescriptor對象的CreateController方法完成的。本章的核心就在于剖析此方法的實現(xiàn)邏輯。
我們可以通過HttpControllerDescriptor的GetCustomAttributes<T>方法得到應(yīng)用在被描述HttpController類型上指定類型的特性列表。調(diào)用另一個方法GetFilters可以獲取應(yīng)用到目標(biāo)HttpController類型上的所有Filter,F(xiàn)ilter在ASP.NET Web API中是一個非常重要的概念,同時也是一種常見的擴(kuò)展方式,我們會在本書第12章“過濾器”中對Filter進(jìn)行單獨(dú)介紹。
HttpControllerDescriptor還具有一個字典類型的只讀屬性Properties,它使我們可以將任何一個對象附加到某個HttpControllerDescriptor上。我們在HttpRequestMessage和HttpConfiguration類型中已經(jīng)看到過了類似的設(shè)計。
我們現(xiàn)在來介紹一下我們創(chuàng)建HttpController類型默認(rèn)繼承的基類ApiController。如下面的代碼片斷所示,除了實現(xiàn)接口IHttpController外,HttpController還采用標(biāo)準(zhǔn)的方式實現(xiàn)了另一個接口IDisposable。如果自定義HttpController需要實現(xiàn)一些資源回收的工作,可以將它們定義在重寫的(受保護(hù)的)虛方法Dispose中。
1: public abstract class ApiController : IHttpController, IDisposable
2: {
3: public virtual Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken);
4: protected virtual void Initialize(HttpControllerContext controllerContext);
5:
6: public void Dispose();
7: protected virtual void Dispose(bool disposing);
8:
9: public HttpControllerContext ControllerContext { get; set; }
10: public HttpConfiguration Configuration { get; set; }
11: public HttpRequestMessage Request { get; set; }
12: public IHttpRouteData RouteData { get; set; }
13:
14: public ModelStateDictionary ModelState { get; }
15: public UrlHelper Url { get; set; }
16: public IPrincipal User { get; }
17: }
ApiController的三個屬性Configuration、Request和RouteData與此HttpControllerContext對象的同名屬性具有相同的引用。表示執(zhí)行當(dāng)前ApiController上下文的HttpControllerContext對象可以通過ControllerContext屬性獲取,這是一個可讀寫的屬性,意味著我們也可以通過設(shè)置該屬性為其指定相應(yīng)的上下文。如果我們沒有對ControllerContext屬性進(jìn)行顯式設(shè)置,該屬性會在第一次被獲取時被自動賦值。
ApiController的只讀屬性ModelState返回一個具有字典數(shù)據(jù)結(jié)構(gòu)的ModelStateDictionary對象,包含其中的數(shù)據(jù)會被以“Model綁定”的形式綁定到目標(biāo)Action方法的對應(yīng)的參數(shù)。除此之外,此ModelStateDictionary還用于保存參數(shù)驗證失敗后的錯誤消息。另一個參數(shù)Url返回一個類型為UrlHelper的對象(UrlHelper定義在命名空間“System.Web.Http.Routing”下),我們利用它可以根據(jù)注冊的HttpRoute和提供的路由變量生成一個完整的URL。
ApiController的User屬性返回當(dāng)前線程的Principal。相信讀者還會記得在本書第3章“消息處理管道”中介紹HttpServer時我們談到:如果當(dāng)前線程的Principal為Null,作為消息處理管道“龍頭”的HttpServer會在SendAsync方法執(zhí)行過程中創(chuàng)建一個空的GenericPrincipal對象作為當(dāng)前線程的“匿名”Principal。所以對于匿名請求來說,這個User屬性會返回這個通過HttpServer設(shè)置的空GenericPrincipal對象。
從上面給出的代碼片斷我們還會看到ApiController包含一個受保護(hù)的Initialize方法,它會根據(jù)由指定HttpControllerContext提供的上下文信息對自身作相應(yīng)的初始化。一旦Initialize方法被成功執(zhí)行,當(dāng)前ApiController對象將處于初始化狀態(tài)。此Initialize在默認(rèn)情況下會在實現(xiàn)的ExecuteAsync方法中被自動調(diào)用。在默認(rèn)情況下,ASP.NET Web API的HttpController激活系統(tǒng)總是創(chuàng)建一個新的HttpController來處理每一個請求。對于其類型繼承自ApiController的HttpController來說,如果在執(zhí)行ExecuteAsync方法的時候發(fā)現(xiàn)當(dāng)前的ApiController已經(jīng)處于“初始化”的狀態(tài),系統(tǒng)會直接拋出一個InvalidOperationException異常。
舉個簡單的例子,假設(shè)我們定義了如下一個繼承自ApiController的DemoController類型,并通如下的方式將原本為受保護(hù)的Initialize方法轉(zhuǎn)換成一個公有方法,以方便我們后續(xù)的調(diào)用。
1: public class DemoController : ApiController
2: {
3: public new void Initialize(HttpControllerContext controllerContext)
4: {
5: base.Initialize(controllerContext);
6: }
7: }
然后我們執(zhí)行如下一段代碼,它的特別之處在于在調(diào)用DemoController對象的ExecuteAsync方法之前調(diào)用了Initialize方法對其作了初始化處理。
1: DemoController controller = new DemoController ();
2: HttpControllerContext controllerContext = new HttpControllerContext(new HttpConfiguration(), new HttpRouteData(new HttpRoute()), new HttpRequestMessage());
新聞熱點(diǎn)
疑難解答