ASP.NET Web API的核心对象:HttpController

对于ASP.NET Web API来说,所谓的Web API定义在继承自ApiController的类中,可能ApiController是大部分读者最为熟悉的类型了。但是我们将ASP.NET Web API下的Controller称为HttpController,它是对所有实现了接口IHttpController的所以Controller类型的统称,而ApiController仅仅视为IHttpController接口的一个实现而已,所以我们会更多地强调HttpController的概念。[本文已经同步到《How ASP.NET Web API Works?》]

目录 
HttpController 
HttpControllerContext 
HttpControllerDescriptor 
ApiController 
实例演示:证明针对每次请求创建的HttpController都是“全新的”

HttpController

既然HttpController指的是所有实现了IHttpController接口的类型,我们自然得下来了解一下这个接口的定义。如下面的代码片断所示,在IHttpController接口中仅仅定义了唯一的方法ExecuteAsync方法,该方法以异步的方式执行HttpController。

public interface IHttpController
{
Task<HttpResponseMessage> ExecuteAsync( HttpControllerContext controllerContext, CancellationToken cancellationToken);
}

与我们在“消息处理管道”中介绍的HttpMessageHandler的SendAsync方法一样,该ExecuteAsync方法返回的依然是一个Task<HttpResponseMessage>对象,代表一个用于处理响应消息的Task。实际上HttpController可以视为对ASP.NET Web API的消息处理管道的延续。如右图所示,作为消息处理管道最后一个HttpMessageHandler的HttpRoutingDispatcher利用自身的HttpControllerDispatcher激活并调用ExecuteAsync方法执行目标HttpController。方法调用返回的Task<HttpResponseMessage>对象正式HttpControllerDispatcher的SendAsync方法的返回值。

与HttpMessageHandler的SendAsync方法不同之处在于:ExecuteAsync方法的参数不再是表示请求的HttpRequestMessage对象,而是一个HttpControllerContext对象。顾名思义,HttpControllerContext表示当前基于HttpController的上下文,HttpController可以认为是在该上下文中执行。

HttpControllerContext

如下面的代码片断所示,通过定义在HttpControllerContext中的属性我们可以得到用于配置消息处理管道的HttpConfiguration,封装路由数据的HttpRouteData,以及表示当前请求的HttpRequestMessage。这三个属性可以在构建HttpControllerContext的时候直接通过构造函数的参数指定,我们也可以先创建一个空的HttpControllerContext对象之后直接对这些属性赋值。

public class HttpControllerContext
{
public HttpControllerContext();
public HttpControllerContext(HttpConfiguration configuration, IHttpRouteData routeData,HttpRequestMessage request); public HttpConfiguration Configuration { get; set; }
public IHttpRouteData RouteData { get; set; }
public HttpRequestMessage Request { get; set; } public IHttpController Controller { get; set; }
public HttpControllerDescriptor ControllerDescriptor { get; set; }
}

既然HttpControllerContext是针对HttpController的上下文,我们自然可以从中得到这个HttpController。我们除了可以通过属性Controller获取或者设置对应的HttpController之外,还可以利用另一个属性ControllerDescriptor获取或者设置用于描述HttpController的System.Web.Http.Controllers.HttpControllerDescriptor对象。

HttpControllerDescriptor

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

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

public class HttpControllerDescriptor
{
public HttpControllerDescriptor();
public HttpControllerDescriptor(HttpConfiguration configuration, string controllerName, Type controllerType); public virtual IHttpController CreateController(HttpRequestMessage request); public virtual Collection<T> GetCustomAttributes<T>() where T: class;
public virtual Collection<T> GetCustomAttributes<T>(bool inherit) where T: class;
public virtual Collection<IFilter> GetFilters(); public HttpConfiguration Configuration { get; set; }
public string ControllerName { get; set; }
public Type ControllerType { get; set; } public virtual ConcurrentDictionary<object, object> Properties { get; }
}

我们说HttpControllerDescriptor具有根据元数据信息创建目标HttpController的能力主要体现在其CreateController方法上。实际上本章的核心内容“HttpController的激活”就体现在HttpControllerDescriptor的这个CreateController方法上,所有后续的内容基本上都是围绕着这个方法展开的。

HttpControllerDescriptor还具有一个字典类型的只读属性Properties,它使我们可以将通过指定一个字符串类型的Key将任何一个对象附加到某个HttpControllerDescriptor,然后通过这个Key人任何情况下将这个附加的对象提取出来。我们在HttpRequestMessage类型中已经看到过了类似的设计。

ApiController

我们现在来介绍一下我们创建HttpController类型默认采用的基类ApiController。如下面的代码片断所示,除了实现接口IHttpController外,HttpController还实现了接口IDisposable。如果在自定义HttpController需要实现一些资源回收的工作,可以将它们定义在重写的Dispose方法中(受保护的虚方法Dispose)。

public abstract class ApiController : IHttpController, IDisposable
{
public virtual Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken);
protected virtual void Initialize(HttpControllerContext controllerContext); public void Dispose();
protected virtual void Dispose(bool disposing); public HttpControllerContext ControllerContext { get; set; }
public HttpConfiguration Configuration { get; set; }
public HttpRequestMessage Request { get; set; }
public IHttpRouteData RouteData { get; set; } public ModelStateDictionary ModelState { get; }
public UrlHelper Url { get; set; }
public IPrincipal User { get; }
}

我们可以通过属性ControllerContext从ApiController对象中获取或者为它设置一个HttpControllerContext对象作为其上下文,另外三个属性Configuration、Request和RouteData与此HttpControllerContext的同名属性引用这同一个对象。

ApiController的只读属性ModelState返回一个具有字典数据结构的ModelStateDictionary对象,包含其中的数据会被以“Model绑定”的形式绑定到目标Action方法的某个参数。除此之外,ApiController的ModelState属性还用于保存参数验证失败后的 错误消息。另一个参数Url返回一个类型为UrlHelper的对象,该对象用于根据注册的HttpRoute和提供的路由变量生成相应的URL。

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

从上面给出的代码片断我们还会看到ApiController包含一个受保护的Initialize方法,该方法根据指定的HttpControllerContext对自身作相应的初始化处理。一旦执行了Initialize方法,当前ApiController对象将处于初始化状态。在默认情况下,此Initialize会在实现的ExecuteAsync方法中被调用。

在默认情况下,ASP.NET Web API的HttpController激活系统总是创建一个新的HttpController来处理每一个请求。对于其类型继承自ApiController的HttpController来说,如果在执行ExecuteAsync方法的时候发现当前的ApiController已经处于“初始化”的状态,会直接抛出一个InvalidOperationException异常。

实例演示:证明针对每次请求创建的HttpController都是“全新的”

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

public class MyApiController : ApiController
{
public new void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
}
}

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

MyApiController controller = new MyApiController();
HttpControllerContext controllerContext = new HttpControllerContext(new HttpConfiguration(), new HttpRouteData(new HttpRoute()), new HttpRequestMessage());
controller.ControllerContext = controllerContext;
controller.Initialize(controllerContext);
controller.ExecuteAsync(controllerContext, new CancellationToken(false));

当执行ApiController的ExecuteAsync方法的时候会抛出如图4-2所示的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都应该是“全新”的。

作者:Artech
出处:http://artech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 

ASP.NET Web API的核心对象:HttpController的更多相关文章

  1. Asp.net Web API 返回Json对象的两种方式

    这两种方式都是以HttpResponseMessage的形式返回, 方式一:以字符串的形式 var content = new StringContent("{\"FileName ...

  2. ASP.NET Web API路由系统:路由系统的几个核心类型

    虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分(定义在程序集System.Web.Http.dll中)已经移除 ...

  3. ASP.NET Web API框架揭秘:路由系统的几个核心类型

    ASP.NET Web API框架揭秘:路由系统的几个核心类型 虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分 ...

  4. ASP.NET Web API 框架研究 核心的消息处理管道

    ASP.NET Web API 的核心框架是一个由一组HttpMessageHandler有序组成的双工消息处理管道:寄宿监听到请求接受后,把消息传入该管道经过所有HttpMessageHandler ...

  5. HttpActionDescriptor,ASP.NET Web API又一个重要的描述对象

    HttpActionDescriptor,ASP.NET Web API又一个重要的描述对象 通过前面对“HttpController的激活”的介绍我们已经知道了ASP.NET Web API通过Ht ...

  6. ASP.NET Web API 路由对象介绍

    ASP.NET Web API 路由对象介绍 前言 在ASP.NET.ASP.NET MVC和ASP.NET Web API这些框架中都会发现有路由的身影,它们的原理都差不多,只不过在不同的环境下作了 ...

  7. ASP.NET Web API标准的“管道式”设计

    ASP.NET Web API的核心框架是一个消息处理管道,这个管道是一组HttpMessageHandler的有序组合.这是一个双工管道,请求消息从一端流入并依次经过所有HttpMessageHan ...

  8. 学习ASP.NET Web API框架揭秘之“HTTP方法重写”

    最近在看老A的<ASP.NET Web API 框架揭秘>,这本书对于本人现阶段来说还是比较合适的(对于调用已经较为熟悉,用其开发过项目,但未深入理解过很多内容为何可以这样“调用”).看到 ...

  9. ASP.NET Web API 框架研究 Web Host模式路由及将请求转出到消息处理管道

    Web Host 模式下的路由本质上还是通过ASP.NET 路由系统来进行路由的,只是通过继承和组合的方式对ASP.NET路由系统的内部的类进行了一些封装,产生自己专用一套类结构,功能逻辑基本都是一样 ...

随机推荐

  1. 基于Hama并联平台Finding a Maximal Independent Set 设计与实现算法

    笔者:白松 NPU学生. 转载请注明出处:http://blog.csdn.net/xin_jmail/article/details/32101483. 本文參加了2014年CSDN博文大赛,假设您 ...

  2. 认识bash这个shell

    我们通过shell将我们输入的命令与内核通信,好让内核可以控制硬件来正确无误地工作bash是我们Linux默认的shell 用户界面(Shell,application)--------核心(Kern ...

  3. 屏蔽webbrowser控件右键的一种方法

    原文:屏蔽webbrowser控件右键的一种方法 Option ExplicitPrivate Declare Sub ZeroMemory Lib "KERNEL32" Alia ...

  4. Facebook Hack 语言 简介

    1. Hack 是什么? Hack 是一种基于HHVM(HipHop VM 是Facebook推出的用来执行PHP代码的虚拟机,它是一个PHP的JIT编译器,同时具有产生快速代码和即时编译的优点.)的 ...

  5. WCF消息交换模式之双工通讯(Duplex)

    WCF消息交换模式之双工通讯(Duplex) 双工通讯Duplex具有以下特点: 1它可以在处理完请求之后,通过请求客户端中的回调进行响应操作 2.消息交换过程中,服务端和客户端角色会发生调换 3.服 ...

  6. Codeforces Round #267 (Div. 2)D(DFS+单词hash+简单DP)

    D. Fedor and Essay time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  7. 调试经验--硬盘U菜

    调试经验--硬盘U菜 随着嵌入式设备功能的开发,随着对存储设备的需求:需要存储大量数据信息.需要在转储数据,U盘升级功能等.     在使用存储设备的过程中,我们遇到一些问题,也总结了些经验: 1.几 ...

  8. jquery插件分类与编写详细讲解

    jquery插件分类与编写详细讲解 1. 插件种类 插件其实就是对现有的方法(或者叫函数)做一个封装,方便重用提高开发效率.   jQeury主要有2种类型   1)实例对象方法插件 开发能让所有的j ...

  9. struts2注解redirect传递参数解决方案时,中国的垃圾问题

    struts2注解redirect传递参数解决方案时,中国的垃圾问题 试过很多方法  tomcat 编码  .字符串转换 .URLEncoder  .. 但是,没有解决方案,然后仔细阅读   stru ...

  10. SQL 中OPENQUERY的使用

    原文:SQL 中OPENQUERY的使用 OpenQuery 是SQL Server用来与其他Server交互的一种技术,通过OpenQuery,SQL Server 可以直接访问其他数据库资源. 而 ...