虽然通过Visual Studio向导在ASP.NET Web API项目中创建的 Controller类型默认派生与抽象类型ApiController,但是ASP.NET Web API框架本身只要求它实现IHttpController接口即可,所以我们将其统称为HttpController。既然HttpController指的是所有实现了IHttpController接口的类型,我们自然得先来了解一下这个接口的定义。如下面的代码片断所示,在IHttpController接口中仅仅定义了唯一的方法ExecuteAsync方法,它以异步的方式执行HttpController,并返回一个Task<HttpResponseMessage>对象。[本文已经同步到《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的消息处理管道的延续。通过“ASP.NET Web API标准的“管道式”设计”的介绍我们知道位于管道末端的是一个HttpRoutingDispatcher对象。当SendAsync方法被执行的时候,HttpRoutingDispatcher会利用隶属于它的HttpControllerDispatcher来激活目标HttpController对象,随后调用该对象的ExecuteAsync方法并将返回的Task<HttpResponseMessage>对象作为返回值。右图揭示了包含激活的HttpController在内的消息处理管道的结构。

一、HttpControllerContext

与HttpMessageHandler的SendAsync方法有所不同,HttpController的ExecuteAsync方法并没有一个表示请求的类型为HttpRequestMessage的参数,取而代之的是一个HttpControllerContext类型的参数。HttpControllerContext定义在命名空间“System.Web.Http.Controllers”下,表示执行HttpController的上下文。

如下面的代码片断所示,通过定义在HttpControllerContext中的属性我们可以得到用于配置消息处理管道的HttpConfiguration对象和封装路由数据的HttpRouteData对象,以及表示当前请求的HttpRequestMessage对象。这三个属性可以在构建HttpControllerContext的时候直接通过构造函数的参数指定,我们也可以先创建一个空的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对象表示执行HttpController的上下文,我们可以通过Controller属性来获取或者设置这个HttpController对象。除此之外,我们还可以利用另一个属性ControllerDescriptor获取或者设置用于描述HttpController的HttpControllerDescriptor对象(类型HttpControllerDescriptor定义在命名空间“System.Web.Http.Controllers”下)。

二、HttpControllerDescriptor

HttpControllerDescriptor封装了某个HttpController类型的元数据,我们可以将它视为某个HttpController类型的描述对象。HttpControllerDescriptor具有根据元数据创建对应HttpController的能力,实际上ASP.NET Web API的HttpController激活系统就是根据HttpControllerDescriptor来创建目标HttpController的。

如下面的代码片断所示,我们可以通过HttpControllerDescriptor的属性Configuration、ControllerName和ControllerType获取当前的HttpConfiguration对象和被描述HttpController的名称和类型。这三个属性可以在构建HttpControllerDescriptor时通过构造函数的参数显式指定,也可以先构建一个空的HttpControllerDescriptor对象,然后手工设置这些属性。

   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具有创建HttpController的能力主要体现在其CreateController方法上,该方法完成了目标方法的激活。换句话说,目标HttpController的激活是通过调用描述它的HttpControllerDescriptor对象的CreateController方法完成的。本章的核心就在于剖析此方法的实现逻辑。

我们可以通过HttpControllerDescriptor的GetCustomAttributes<T>方法得到应用在被描述HttpController类型上指定类型的特性列表。调用另一个方法GetFilters可以获取应用到目标HttpController类型上的所有Filter,Filter在ASP.NET Web API中是一个非常重要的概念,同时也是一种常见的扩展方式,我们会在本书第12章“过滤器”中对Filter进行单独介绍。

HttpControllerDescriptor还具有一个字典类型的只读属性Properties,它使我们可以将任何一个对象附加到某个HttpControllerDescriptor上。我们在HttpRequestMessage和HttpConfiguration类型中已经看到过了类似的设计。

三、ApiController

我们现在来介绍一下我们创建HttpController类型默认继承的基类ApiController。如下面的代码片断所示,除了实现接口IHttpController外,HttpController还采用标准的方式实现了另一个接口IDisposable。如果自定义HttpController需要实现一些资源回收的工作,可以将它们定义在重写的(受保护的)虚方法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对象的同名属性具有相同的引用。表示执行当前ApiController上下文的HttpControllerContext对象可以通过ControllerContext属性获取,这是一个可读写的属性,意味着我们也可以通过设置该属性为其指定相应的上下文。如果我们没有对ControllerContext属性进行显式设置,该属性会在第一次被获取时被自动赋值。

ApiController的只读属性ModelState返回一个具有字典数据结构的ModelStateDictionary对象,包含其中的数据会被以“Model绑定”的形式绑定到目标Action方法的对应的参数。除此之外,此ModelStateDictionary还用于保存参数验证失败后的错误消息。另一个参数Url返回一个类型为UrlHelper的对象(UrlHelper定义在命名空间“System.Web.Http.Routing”下),我们利用它可以根据注册的HttpRoute和提供的路由变量生成一个完整的URL。

ApiController的User属性返回当前线程的Principal。相信读者还会记得在本书第3章“消息处理管道”中介绍HttpServer时我们谈到:如果当前线程的Principal为Null,作为消息处理管道“龙头”的HttpServer会在SendAsync方法执行过程中创建一个空的GenericPrincipal对象作为当前线程的“匿名”Principal。所以对于匿名请求来说,这个User属性会返回这个通过HttpServer设置的空GenericPrincipal对象。

从上面给出的代码片断我们还会看到ApiController包含一个受保护的Initialize方法,它会根据由指定HttpControllerContext提供的上下文信息对自身作相应的初始化。一旦Initialize方法被成功执行,当前ApiController对象将处于初始化状态。此Initialize在默认情况下会在实现的ExecuteAsync方法中被自动调用。在默认情况下,ASP.NET Web API的HttpController激活系统总是创建一个新的HttpController来处理每一个请求。对于其类型继承自ApiController的HttpController来说,如果在执行ExecuteAsync方法的时候发现当前的ApiController已经处于“初始化”的状态,系统会直接抛出一个InvalidOperationException异常。

举个简单的例子,假设我们定义了如下一个继承自ApiController的DemoController类型,并通如下的方式将原本为受保护的Initialize方法转换成一个公有方法,以方便我们后续的调用。

   1: public class DemoController : ApiController

   2: {

   3:     public new void Initialize(HttpControllerContext controllerContext)

   4:     {

   5:         base.Initialize(controllerContext);

   6:     }

   7: }

然后我们执行如下一段代码,它的特别之处在于在调用DemoController对象的ExecuteAsync方法之前调用了Initialize方法对其作了初始化处理。

   1: DemoController controller = new DemoController ();

   2: HttpControllerContext controllerContext = new HttpControllerContext(new HttpConfiguration(), new HttpRouteData(new HttpRoute()), new HttpRequestMessage());

   3: controller.ControllerContext = controllerContext;

   4: controller.Initialize(controllerContext);

   5: controller.ExecuteAsync(controllerContext, new CancellationToken(false));

当执行ApiController的ExecuteAsync方法的时候会抛出如右图所示的InvalidOperation异常,并提示“Cannot reuse an 'ApiController' instance. 'ApiController' has to be constructed per incoming message. Check your custom 'IHttpControllerActivator' and make sure that it will not manufacture the same instance.”错误消息已经表明了ApiController是不能“重用”的,用于处理每一个请求的ApiController都应该是“全新”的。

ASP.NET Web API中的Controller的更多相关文章

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

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

  2. ASP.NET Web API中的依赖注入

    什么是依赖注入 依赖,就是一个对象需要的另一个对象,比如说,这是我们通常定义的一个用来处理数据访问的存储,让我们用一个例子来解释,首先,定义一个领域模型如下: namespace Pattern.DI ...

  3. 【ASP.NET Web API教程】5.5 ASP.NET Web API中的HTTP Cookie

    原文:[ASP.NET Web API教程]5.5 ASP.NET Web API中的HTTP Cookie 5.5 HTTP Cookies in ASP.NET Web API 5.5 ASP.N ...

  4. 【ASP.NET Web API教程】4.3 ASP.NET Web API中的异常处理

    原文:[ASP.NET Web API教程]4.3 ASP.NET Web API中的异常处理 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本系列教程,请先看前面的内 ...

  5. 【ASP.NET Web API教程】4.1 ASP.NET Web API中的路由

    原文:[ASP.NET Web API教程]4.1 ASP.NET Web API中的路由 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. ...

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

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

  7. ASP.NET Web API中的Routing(路由)

    [译]Routing in ASP.NET Web API 单击此处查看原文 本文阐述了ASP.NET Web API是如何将HTTP requests路由到controllers的. 如果你对ASP ...

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

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

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

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

随机推荐

  1. Xamarin.Android 应用程序配置

    * 在 Xamarin 中 Android 清单文件的内容一般不通过手动编辑,而是由编译器根据 项目属性设置 和 一系列 特性类 自动生成 1. 应用程序在android启动器中显示的名称设置: 主活 ...

  2. Main()

    P25 “每一个c#可执行文件都必须有一个入口——Main()方法” 我一直对这个Main()方法有一些疑问. 那就是这里头的参数. 在JAVA里,main(String args[])中的参数是绝对 ...

  3. 第9章 用内核对象进行线程同步(3)_信号量(semaphore)、互斥对象(mutex)

    9.5 信号量内核对象(Semaphore) (1)信号量的组成 ①计数器:该内核对象被使用的次数 ②最大资源数量:标识信号量可以控制的最大资源数量(带符号的32位) ③当前资源数量:标识当前可用资源 ...

  4. Dinic算法模板

    详解:http://blog.csdn.net/wall_f/article/details/8207595 算法时间复杂度:O(E * V * V) #include <cstdio> ...

  5. Qt使用一个事件队列对所有发出的事件进行维护(QObject的event()函数相当于dispatch函数),用EventLabel 继承QLabel作为例子(简单明了) good

    事件(event)是由系统或者 Qt 本身在不同的时刻发出的.当用户按下鼠标.敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件.一些事件在对用户操作做出响应时发出,如键盘事件等:另一些事 ...

  6. IOS开发的国际化

    一 app中内容的国际化 1 添加简体中文支持(默认只有英文)         在xcode的PROJECT->Info->Localizations下添加简体中文的支持.   2新建St ...

  7. ubuntu 11.04 源 更新不了,全显示ign、404

    原文地址:http://blog.csdn.net/enjio/article/details/11603373   ubuntu 11.04 源 更新不了 分类: 开发相关2013-09-12 14 ...

  8. 如何利用log4Net自定义属性配置功能记录完整的日志信息

    log4Net作为专业的log记录控件,对于它的强大功能大家一定不陌生.下面我将详细介绍如何利用其自定义属性,让日志信息更完整. 一,创建测试工程,log4Net组件可以自己从网上下载,也可通过Nug ...

  9. 使用Python脚本强化LLDB调试器

    LLDB是Xcode自带的调试器,作为一个iOS应用开发程序员,平时我在开发应用时会使用LLDB来调试代码.在逆向应用时,也会用到LLDB来跟踪应用的执行过程. LLDB还内置了一个Python解析器 ...

  10. Fast scroller styles

    <!-- Fast scroller styles --> <!-- Drawable to use as the fast scroll thumb. --> <att ...