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. java学习笔记2——Eclipse的安装及汉化图解

    Eclipse的安装 有了JDK,你可以编译Java源码,运行Java程序,但是还没有代码编辑器,没有版本管理工具,也不能方便的管理工程文件,不能与团队协作.安装Eclipse,你才能完成这些工作. ...

  2. MyReport报表引擎2.7.6.7新功能

    新增二维码控件PDF417 设计器新增数据选项卡,可以拖放字段进行绑定   相关链接 MyReport演示.产品站点 相关文章 MyReport专栏

  3. 响应式web前端框架Foundation & Bootstrap 对比

    Foundation & Bootstrap都是易用.强大且灵活的前端框架,用于构建基于任何设备上的 Web 应用.提供流式布局,及多种 js UI 组件,如导航.表单.按钮.Tabs 等等. ...

  4. ASP.NET MVC3中Model验证

    原文:ASP.NET MVC3中Model验证 概述 上节我们学习了Model的数据在界面之间的传递,但是很多时候,我们在数据传递的时候为了确保数据的有效性,不得不给Model的相关属性做基本的数据验 ...

  5. 解决方案命名空间“System.Web.Mvc”中不存在类型或命名空间名称“Ajax”(是否缺少程序集引用?)

    首先对System.Web.Mvc这个dll文件重新引用本地的,添加引用,搜索mvc就可以出来,选择相应的版本.如果还不能正常运行, 然后右键打开这个项目引用System.Web.Mvc, 将复制本地 ...

  6. 检测浏览器版本类型的JavaScript代码,终极版

    下面的JavaScript代码,不仅可以判断PC端浏览器类型,还可以判断安卓.iOS.其他智能手机.平板电脑或游戏系统. 说废话貌似不是我的风格哈,直接上代码吧: var client = funct ...

  7. lsof基本使用

    当你想在计算机上启动一个服务,电脑已经建议"port already in use",此时,可以使用lsof命令查看占用端口的进程(lsof -i:port). lsof这是LiS ...

  8. 干净的架构The Clean Architecture

    干净的架构The Clean Architecture 这是著名软件大师Bob大叔提出的一种架构,也是当前各种语言开发架构.干净架构提出了一种单向依赖关系,从而从逻辑上形成一种向上的抽象系统. 我们经 ...

  9. ASP.NET中Partial Class部分类

    原文:ASP.NET中Partial Class部分类 如果您在开发项目的公用函数库,用于公用函数库的内容越丰富越好,但这不可能一下子就写好,需要一点一点的积累,这时就可以使用Partial Clas ...

  10. CreateFont详细解释

    CFont * f;    f = new CFont;    f->CreateFont(10, // nHeight         0, // nWidth         0, // n ...