ASP.NET Web API Controller 是怎么建成的
先看ASP.NET Web API 讯息管线:
註:为了避免图片太大以至于超过版面,上图中的「HTTP 讯息处理程序」区块省略了 HttpRoutingDispatcher 处理路由分派的部分。「控制器」区块则省略了筛选条件(filter)的处理细节。微软网站有提供一份比较完整的 Web API 讯息处理流程图,网址是 http://www.microsoft.com/en-us/download/details.aspx?id=36476。
此讯息管线架构图分为三层,由上至下,分别是装载(Hosting)、讯息处理程序(Message Handlers)、以及控制器(Controller)。图中的红色实心箭头代表 HTTP 请求讯息,虚线箭头代表 HTTP 响应消息。讯息处理流程如下:
- 当客户端对服务器发出的 HTTP 请求开始进入 ASP.NET Web API 框架时,该 HTTP 请求讯息会被包装成HttpRequestMessage对象,并且进入图中最顶端「装载」方块的HttpServer(web 装载)或HttpSelfHostServer(自我装载)。接着该讯息便流入管线的下一个阶段,直到整个讯息流程处理完毕,会得到一个代表 HTTP 响应消息的 HttpResponseMessage对象,并将此对象的讯息内容传回客户端。
- HttpRequestMessage对象进入「讯息处理程序」管线。在此阶段,HTTP 讯息行经数个讯息处理程序(message handlers),并且在返回 HTTP 响应消息时以相反的顺序执行。
- 在各个讯息处理程序之后,HTTP 请求讯息接着会传递给HttpControllerDispatcher,并且由这个对象来建立 Web API controller,然后将 HTTP 请求传递给 controller 对象(图中标示「(A) 建立 controller」的步骤)。
- Controller 会先决定目标动作方法(即图中标示「(B) 选择 action」的步骤),然后呼叫它。动作方法将负责产生响应内容,之后便依前述管线流程的反方向沿路返回。
以上便是 Web API HTTP 讯息管线的大致处理流程。
Web API Controller 是怎样建成的?
刚才只说明了 Web API HTTP 讯息管线的大致处理流程,而欲注入相依对象至 controller 类别的建构函式,或从中动些手脚来改变预设行为,必得了解 Web API 框架建立 controller 的内部过程。本节将进一步说明其中的复杂环节,其中会反复提及多个抽象接口,第一次阅读时可能略感吃力,并难免心生疑惑,但等到实际写过、跑过一遍后面的范例程序,再回头来看这一节的说明,整个拼图应该就会渐渐明朗了。
刚才提到,HttpControllerDispatcher会建立目标 controller 对象,亦即先前 ASP.NET Web
API 管线架构图中标示「(A) 建立 controller」的步骤。此步骤其实包含两件工作:
- 解析目标 controller。亦即决定该使用哪一个 controller 类别。
- 建立目标 controller 类别的实例,并将 HTTP 请求(HttpRequestMessage对象)传递给它,以便由 controller 进行后续处理。
首先,「解析目标 controller」的工作主要是从应用程序的 DLL 组件中寻找所有可用的 controller 类别,再从中选择一个与当前 HTTP request 匹配的。其处理逻辑如下图所示:
说明:
- 图中下方的IAssembliesResolver对象的GetAssemblies方法将提供应用程序的组件列表,并由IHttpControllerTypeResolver对象的GetControllerTypes方法取得可用的 controller 类别清单。
- IHttpControllerSelector负责决定要选择哪一个 controller 类别,然后返回一个包含其型别信息的HttpControllerDescriptor对象给HttpControllerDispatcher。
从确定目标 controller 型别之后,到建立完成 controller 实例的过程中,还有经过一些核心标准接口所提供的扩充点。底下再用一张 UML 活动图搭配 Web API 原始码的方式来解构其内部处理过程。
说明如下(与上图中的数字编号对应):
(1) HttpControllerDispatcher透过IHttpControllerSelector对象的SelectController方法来取得目标 controller 型别信息,这型别信息是包在一个HttpControllerDescriptor 对象里。
(2) HttpControllerDispatcher接着呼叫HttpControllerDescriptor对象的CreateController 方法,而该方法又会去呼叫ServicesContainer对象的GetHttpControllerActivator方法来取得IHttpControllerActivator对象。以下程序片段摘自 Web API 原始码,涵盖了此步骤至下一步骤的部分逻辑:
// HttpControllerDescriptor 类别的 CreateController 方法。
public virtual IHttpController CreateController(HttpRequestMessage request)
{
IHttpControllerActivator activator = Configuration.Services.GetHttpControllerActivator();
IHttpController instance = activator.Create(request, this, ControllerType);
return instance;
}
(3) 取得IHttpControllerActivator对象之后,便接着呼叫它的Create方法,而此方法会呼叫自己的GetInstanceOrActivator方法,以便取得 controller 实例。以下程序片段摘自DefaultHttpControllerActivator类别的原始码,我把错误处理以及快取机制的部分拿掉,并加上了中文批注:
// DefaultHttpControllerActivator 類別的 Create 方法(重點摘錄)
public IHttpController Create(HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
Func<IHttpController> activator; IHttpController controller =
GetInstanceOrActivator(request, controllerType, out activator);
if (controller != null)
{
// 註冊至 Web API 框架的 dependency resolver
// 已經建立此 controller 型別的執行個體。
return controller; // 那就直接使用此物件。
}
// 目標 controller 物件尚未建立
return activator(); // 那就用 GetInstanceOrActivator 方法傳回的委派來建立物件
}
(4) IHttpControllerActivator对象的GetInstanceOrActivator方法会呼叫HttpRequestMessage 的扩充方法GetDependencyScope来取得与当前 request 关联的IDependencyScope对象
(其实就是个 Service Locator),并利用它的GetService方法来取得 controller 对象。若 GetService方法并未传回 controller 对象,而是传回null(代表无法解析服务型别),则退而求其次,改用型别反射(reflection)机制来建立 controller 对象。一样搭配原始码来看:
// 摘自 DefaultHttpControllerActivator.cs
private static IHttpController GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, out Func<IHttpController>> activator)
{
// 若 dependency scope 有传回 controller 对象,便使用它。
IHttpController instance = (IHttpController)request.GetDependencyScope().GetService(controllerType);
if (instance != null)
{
activator = null;
return instance;
} // 否则,建立一个委派来创建此型别的实例。
activator = TypeActivator.Create<IHttpController>(controllerType);
return null;
}
其中的request.GetDependencyScope()就是对应到刚才说的「呼叫HttpRequestMessage 的扩充方法 GetDependencyScope 来取得与当前 request 关联的 IDependencyScope 对象。」而这里实际取得的IDependencyScope对象会是 Web API 框架提供的预设实作: EmptyResolver。从类别名称可知,这类别其实啥事也没做——它的GetService方法一律传回null。因此,在预设情况下,Web API 框架会一律使用型别反射(reflection)机制来建立 controller 对象,而这也就是为什么我们的 controller 类别一定要有预设建构函式(default constructor)的缘故。
大致上,controller 对象就是这么建成的。
本文摘自《.NET 相依性注入》一书的第 5 章。
ASP.NET Web API Controller 是怎么建成的的更多相关文章
- Professional C# 6 and .NET Core 1.0 - Chapter 42 ASP.NET Web API
本文内容为转载,重新排版以供学习研究.如有侵权,请联系作者删除. 转载请注明本文出处: -------------------------------------------------------- ...
- [水煮 ASP.NET Web API2 方法论](1-1)在MVC 应用程序中添加 ASP.NET Web API
问题 怎么样将 Asp.Net Web Api 加入到现有的 Asp.Net MVC 项目中 解决方案 在 Visual Studio 2012 中就已经把 Asp.Net Web Api 自动地整合 ...
- On the nightmare that is JSON Dates. Plus, JSON.NET and ASP.NET Web API
Ints are easy. Strings are mostly easy. Dates? A nightmare. They always will be. There's different c ...
- ASP.NET Web API中的Controller
虽然通过Visual Studio向导在ASP.NET Web API项目中创建的 Controller类型默认派生与抽象类型ApiController,但是ASP.NET Web API框架本身只要 ...
- ASP.NET Web API的Controller是如何被创建的?
Web API调用请求的目标是定义在某个HttpController类型中的某个Action方法,所以消息处理管道最终需要激活目标HttpController对象.调用请求的URI会携带目标HttpC ...
- [ASP.NET Web API]如何Host定义在独立程序集中的Controller
通过<ASP.NET Web API的Controller是如何被创建的?>的介绍我们知道默认ASP.NET Web API在Self Host寄宿模式下用于解析程序集的Assemblie ...
- 总体介绍ASP.NET Web API下Controller的激活与释放流程
通过<ASP.NET Web API的Controller是如何被创建的?>我们已经对HttpController激活系统的核心对象有了深刻的了解,这些对象包括用于解析程序集和有效Http ...
- ASP.NET Web API 框架研究 Controller实例的销毁
我们知道项目中创建的Controller,如ProductController都继承自ApiController抽象类,其又实现了接口IDisposable,所以,框架中自动调用Dispose方法来释 ...
- ASP.NET Web API下Controller激活
一.HttpController激活流程 对于组成ASP.NET Web API核心框架的消息处理管道来说,处于末端的HttpMessageHandler是一个HttpRoutingDispatche ...
随机推荐
- 【rlz000】字串找数
Time Limit: 3 second Memory Limit: 2 MB 问题描述 输入一个字符串,内有数字和非数字字符.如A123X456Y7A,302ATB567BC,统计共有多少个整数, ...
- 【BZOJ 2534】Uva10829L-gap字符串
[链接]h在这里写链接 [题意] 让你找出uvu形式的字符串个数. v非空且长度为L,且u也非空 [题解] 之前做过一道相同的题. 枚举u的长度. 然后从u,2 ...
- dp_Pku1887
<span style="color:#000099;">/* A - 单纯dp 示例 Time Limit:1000MS Memory Limit:30000KB 6 ...
- wpf设置设计时的ViewModel
原文:wpf设置设计时的ViewModel wpf mvvm开发中,有些情况下,你的view不一定设置DataContext,但是你又想在设计阶段,能够自动提示你的view绑定的viewmodel的内 ...
- twemproxy架构分析——剖析twemproxy代码前编
twemproxy背景 在业务量剧增的今天,单台高速缓存服务器已经无法满足业务的需求, 而相较于大容量SSD数据存储方案,缓存具备速度和成本优势,但也存在数据安全性的挑战.为此搭建一个高速缓存服务器集 ...
- WPF 实现测量显示文本长度
原文:WPF 实现测量显示文本长度 以工具类的方式实现: using System; using System.Windows; using System.Windows.Media; using S ...
- SSH深度历险记(九) Struts2+DWZ+Uploadify多文件(文件和图片等。)上传
在gxpt_uas系统,为了实现文件(文件和图片等.,灵活配置)批量上传到mongodb,在学习的过程中,知道mongodb,功能,实现思路:在DWZ的基础上參考官方的实例结合现有的GXPT来实现,期 ...
- 受限玻尔兹曼机(RBM)以及对比散度(CD)
1. RBM 的提出 BM 的缺点: 计算时间漫长,尤其是无约束自由迭代的负向阶段: 对抽样噪音敏感: 流行软件的不支持: 受限玻尔兹曼机(Restricted Boltzmann Machine,简 ...
- 微信小程序之商品属性分类
所提及的购物数量的加减,现在说说商品属性值联动选择. 为了让同学们有个直观的了解,到电商网截了一个图片,就是红圈所示的部分 现在就为大家介绍这个小组件,在小程序中,该如何去写 下图为本项目的图: wx ...
- QT 窗体控件的透明度设置(三种方法)
整个窗体 当设置QT的窗体(QMainWindow, QDialog)时,直接用 targetForm->setWindowOpacity() 函数即可实现,效果为窗体及窗体内所有控件都透明 ...