zoukankan      html  css  js  c++  java
  • 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 响应消息。讯息处理流程如下:

    1. 当客户端对服务器发出的 HTTP 请求开始进入 ASP.NET Web API 框架时,该 HTTP 请求讯息会被包装成HttpRequestMessage对象,并且进入图中最顶端「装载」方块的HttpServer(web 装载)或HttpSelfHostServer(自我装载)。接着该讯息便流入管线的下一个阶段,直到整个讯息流程处理完毕,会得到一个代表 HTTP 响应消息的 HttpResponseMessage对象,并将此对象的讯息内容传回客户端。
    2. HttpRequestMessage对象进入「讯息处理程序」管线。在此阶段,HTTP 讯息行经数个讯息处理程序(message handlers),并且在返回 HTTP 响应消息时以相反的顺序执行。
    3. 在各个讯息处理程序之后,HTTP 请求讯息接着会传递给HttpControllerDispatcher,并且由这个对象来建立 Web API controller,然后将 HTTP 请求传递给 controller 对象(图中标示「(A) 建立 controller」的步骤)。
    4. Controller 会先决定目标动作方法(即图中标示「(B) 选择 action」的步骤),然后呼叫它。动作方法将负责产生响应内容,之后便依前述管线流程的反方向沿路返回。

    以上便是 Web API HTTP 讯息管线的大致处理流程。

    Web API Controller 是怎样建成的?

    刚才只说明了 Web API HTTP 讯息管线的大致处理流程,而欲注入相依对象至 controller 类别的建构函式,或从中动些手脚来改变预设行为,必得了解 Web API 框架建立 controller 的内部过程。本节将进一步说明其中的复杂环节,其中会反复提及多个抽象接口,第一次阅读时可能略感吃力,并难免心生疑惑,但等到实际写过、跑过一遍后面的范例程序,再回头来看这一节的说明,整个拼图应该就会渐渐明朗了。

    刚才提到,HttpControllerDispatcher会建立目标 controller 对象,亦即先前 ASP.NET Web

    API 管线架构图中标示「(A) 建立 controller」的步骤。此步骤其实包含两件工作:

    1. 解析目标 controller。亦即决定该使用哪一个 controller 类别。
    2. 建立目标 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 章。

  • 相关阅读:
    HBaseTDG ClientAPI The Basics
    Hadoop TDG 3 – MR Features
    NoSQL Data Modeling Techniques
    配置SharePoint 2010的User Profile Synchronization连接到第三方的LDAP服务
    Failed to load data access DLL, 0x80004005
    如何开放HyperV服务的远程连接权限给别人?
    SharePoint 2010 Search 架构 已完工
    如何对SharePoint里OOB的JavaScript文件进行Debug 之一
    UPA Sync Service启动之后立即自动停止
    SharePoint 2010服务器场的防火墙需要开放哪些端口?
  • 原文地址:https://www.cnblogs.com/huanlin/p/how-webapi-controller-is-created.html
Copyright © 2011-2022 走看看