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 ...
随机推荐
- pycharm输出乱码如\xe9\x9d\x92\xe8\x9b\x99\xe7\x8e\x8b\xe5\xad\x90转成中文
转自:https://blog.csdn.net/baidu_19473529/article/details/54949453 利用Python解决unicode编码问题,有些json在控制台打印也 ...
- android:layout_gravity和android:gravity属性的差别
gravity的中文意思就是"重心",就是表示view横向和纵向的停靠位置 android:gravity:是对view控件本身来说的,是用来设置view本身的文本应该显示在vie ...
- 数据批量插入MSSQL
MSSQL数据批量插入优化详细 序言 现在有一个需求是将10w条数据插入到MSSQL数据库中,表结构如下,你会怎么做,你感觉插入10W条数据插入到MSSQL如下的表中需要多久呢? 或者你的批量数据 ...
- SQLite 适用场景
SQLite最佳试用场合 网站 作为数据库引擎SQLite适用于中小规模流量的网站(也就是说, 99.9%的网站). SQLite可以处理多少网站流量在于网站的数据库有多大的压力. 通常来说, 如果一 ...
- 在vs code中使用dotnet watch run
只需要在csproj文件中加入一行: <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App&quo ...
- 常用user agent
测试user agnet的网站: http://whatsmyuseragent.com/ Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) ...
- Excel 2013永久取消超链接
原文:Excel 2013永久取消超链接 在使用Excel的过程中,Excel会自动将网址转换为超链接,操作不当,容易误点,引起不必要的错误, 那么本篇博客就总结下如何在Excel 2013里永久取消 ...
- Codeforces 35E Parade 扫描线 + list
主题链接:点击打开链接 意甲冠军:特定n矩阵(总是接近底部x轴) 然后找到由上面的矩阵所包围的路径,的点 给定n 以下n行给定 y [x1, x2] 表示矩阵的高度和2个x轴坐标 思路: 扫描线维护每 ...
- [ACM] POJ 3096 Surprising Strings (map使用)
Surprising Strings Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 5783 Accepted: 379 ...
- 使用WPF技术模拟手机界面
原文:使用WPF技术模拟手机界面 1. 前言 WPF(Windows Presentation Foundation),即"Windows呈现基础",它的目的非常明确,就是用来把数 ...