Web APi之控制器创建过程及原理解析(八)

前言

中秋歇了歇,途中也时不时去看看有关创建控制器的原理以及解析,时间拖得比较长,实在是有点心有余而力不足,但又想着既然诺下了要写完原理一系列,还需有始有终。废话少说,直入主题。

HttpControllerDispatcher

遗留问题 :在第六篇末尾所给图中有一个HttpControllerDispatcher未进行叙述,在本篇中去叙述正合时宜。

在第六篇我们也说过在Web API消息处理管道中的最后一个处理程序是HttpRoutingDispacher,它被用来激活控制器,这样说虽然没错,但是不够具体,实际上是利用隶属于它的HttpControllerDispatcher来激活控制器,而当我们调用HttpRoutingDispatcher中的SendAsync方法来处理请求,实际上是调用HttpControllerDispatcher中的一个私有方法 SendAsyncInternal 来处理请求,下面我们来看看这个方法的定义:

 

最重要的当属以上这三个部分,下面我们一一来进行解析(放松心情,容我娓娓道来)。

通过上面我们看到了CreateController这个方法,是的,你没看错,就是这个方法里面生成了APIController,那是不是就讲完了呢?当然不是,你得知道到底是怎样创建的,在此方法中只是进行了调用了方法来创建APIController控制器而已,其中的细节还得我们慢慢去摸索。

第一步:HttpControllerDescriptor descriptor = this.ControllerSelector.SelectController(request)

我们首先看看这个属性ControllerSelector的定义:

private IHttpControllerSelector ControllerSelector
{
    get
    {
        if (this._controllerSelector == null)
        {
            this._controllerSelector = this._configuration.Services.GetHttpControllerSelector();
        }
        return this._controllerSelector;
    }
}

这个属性是通过返回值为HttpConfiguration的属性_configuration来获取,接下来我们来看看HttpConfiguration的定义,列举出下面最重要的两个属性:

public IDependencyResolver DependencyResolver { get; set; }

public ServicesContainer Services { get; internal set; }

ServicesContainer是服务的容器,在消息处理管道中每一个环节都是通过注册组件来完成相应的任务,而这些组件都是通过实现了某个接口而创建的,而服务容器正是这些接口的基点,ServicesContainer就是所谓的DI容器,将我们需要的服务注册到其中,但是它只是预定义了许多接口,(IDependencyResolver作用也类似于ServicesContainer)所以我们需要看其子类 DefaultServices 的具体实现,我们来看看其子类构造函数传入HttpConfiguration的比较重要的一个定义:

 this.SetSingle<IHttpControllerSelector>(new DefaultHttpControllerSelector(configuration));

从此句知,上述的IHttpControllerSelector接口所定义的属性ControllerSelector就是我们注册的 DefaultHttpControllerSelector ,同时依上述也知,SelectController是通过此类而实现,所以我们再来看看此类中关于此方法的定义及实现:

 1 public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request)
 2 {
 3   HttpControllerDescriptor descriptor;
 4   string controllerName = this.GetControllerName(request);
 5   if (this._controllerInfoCache.Value.TryGetValue(controllerName, out descriptor))
 6     {
 7         return descriptor;
 8     }
 9     ICollection<Type> controllerTypes = this._controllerTypeCache.GetControllerTypes(controllerName);
10 }

我们首先看看是红色标记第一个,它是如何获得控制器名controllerName的,查看其GetControllerName方法,如下:

public virtual string GetControllerName(HttpRequestMessage request)
{
    IHttpRouteData routeData = request.GetRouteData();
    string str = null;
    routeData.Values.TryGetValue<string>("controller", out str);
    return str;
}

我们知道当请求过来时会与注册的路由进行匹配并解析并将其数据封装到RouData对象中,以路由模板中大括号的值为键,以请求的URL中对应的控制器为值,所以上述就获取RouteData中的键所对应的值并返回控制器名。  

接下来我们再来看红色标记第二个属性_controllerInfoCache,此属性存在于DefaultHttpControllerSelector,所以还是仔细看看该类定义:

 1 public class DefaultHttpControllerSelector : IHttpControllerSelector
 2 {
 3     // Fields
 4     private readonly HttpConfiguration _configuration;
 5     private readonly Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>> _controllerInfoCache;
 6     private readonly HttpControllerTypeCache _controllerTypeCache;
 7     private const string ControllerKey = "controller";
 8     public static readonly string ControllerSuffix;
 9
10     // Methods
11     static DefaultHttpControllerSelector();
12     public DefaultHttpControllerSelector(HttpConfiguration configuration);
13     private static Exception CreateAmbiguousControllerException(IHttpRoute route, string controllerName, ICollection<Type> matchingTypes);
14     public virtual IDictionary<string, HttpControllerDescriptor> GetControllerMapping();
15     public virtual string GetControllerName(HttpRequestMessage request);
16     private ConcurrentDictionary<string, HttpControllerDescriptor> InitializeControllerInfoCache();
17     public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request);
18 }

根据_controllerInfoCache,我们从表面意思知是控制器信息缓存,这个得重点讲述下,为何?因为当请求过来时,我们会选择控制器,但是要选择哪个控制器呢,因为对于控制器的类型解析如果使用反射将需要耗费大量的时间,肯定是惨不忍睹的,所以我们就对解析出来的控制类型进行缓存,这将大大提高效率并节约时间,所以由上知,控制器选择器ControllerSelector的作用是两个:

  • 根据请求选择相应的选择控制器

  • 生成控制器缓存  

由上述知控制器的缓存类型,即属性_controllerInfoCache的返回类型  Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>>  ,该字典中字符串为控制器名称,而HttpControllerDescriptor是对控制器的相关描述类型,这个类型我们下面讲,那它是怎么获取到缓存的呢?这个时候我们就要看看上述红色标记中关于DefaultHttpControllerSelector的构造函数,查看如下:

 1 public DefaultHttpControllerSelector(HttpConfiguration configuration)
 2 {
 3     if (configuration == null)
 4     {
 5         throw Error.ArgumentNull("configuration");
 6     }
 7     this._controllerInfoCache = new Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>>(new Func<ConcurrentDictionary<string, HttpControllerDescriptor>>(this.InitializeControllerInfoCache));
 8     this._configuration = configuration;
 9     this._controllerTypeCache = new HttpControllerTypeCache(this._configuration);
10 }

由上知,是通过使用延迟加载技术来对控制器缓存进行了创建而创建则是通过 InitializeControllerInfoCache 方法来进行,下面我们继续看看该方法的实现:

 1 private ConcurrentDictionary<string, HttpControllerDescriptor> InitializeControllerInfoCache()
 2 {
 3     ConcurrentDictionary<string, HttpControllerDescriptor> dictionary = new ConcurrentDictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
 4     HashSet<string> set = new HashSet<string>();
 5     foreach (KeyValuePair<string, ILookup<string, Type>> pair in this._controllerTypeCache.Cache)
 6     {
 7         string key = pair.Key;
 8         foreach (IGrouping<string, Type> grouping in pair.Value)
 9         {
10             foreach (Type type in grouping)
11             {
12                 if (dictionary.Keys.Contains(key))
13                 {
14                     set.Add(key);
15                     break;
16                 }
17                 dictionary.TryAdd(key, new HttpControllerDescriptor(this._configuration, key, type));
18             }
19         }
20     }
21     foreach (string str2 in set)
22     {
23         HttpControllerDescriptor descriptor;
24         dictionary.TryRemove(str2, out descriptor);
25     }
26     return dictionary;
27 }

注意上述该方法中的红色标记知,最终是通过遍历控制器类型缓存而来,所以我们的重点就是控制器类型缓存 HttpControllerTypeCache 。

那问题来了,该控制器缓存类是如何而来的呢?  

这个时候就要用到服务容器ServiceContainer了即该子类DefaultServices注入的服务,如下:

this.SetSingle<IHttpControllerTypeResolver>(new DefaultHttpControllerTypeResolver());

再来看看此类的定义:

 1 public class DefaultHttpControllerTypeResolver : IHttpControllerTypeResolver
 2 {
 3     // Fields
 4     private readonly Predicate<Type> _isControllerTypePredicate;
 5
 6     // Methods
 7     public DefaultHttpControllerTypeResolver();
 8     public DefaultHttpControllerTypeResolver(Predicate<Type> predicate);
 9     public virtual ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver);
10     internal static bool IsControllerType(Type t);
11
12     // Properties
13     protected Predicate<Type> IsControllerTypePredicate { get; }
14 }

通过上述方法根据过滤条件等来加载指定程序集中所有符合条件的控制其类型(ControllerTypes)。这个时候控制器类型缓存就急不可耐了,一旦加载了所有控制器类型,它就会通过构造函数获得所有控制类型并进行分组。如下:

private Dictionary<string, ILookup<string, Type>> InitializeCache()
{
    IAssembliesResolver assembliesResolver = this._configuration.Services.GetAssembliesResolver();
    return this._configuration.Services.GetHttpControllerTypeResolver().GetControllerTypes(assembliesResolver).GroupBy<Type, string>(t => t.Name.Substring(0, t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length), StringComparer.OrdinalIgnoreCase).ToDictionary<IGrouping<string, Type>, string, ILookup<string, Type>>(g => g.Key, g => g.ToLookup<Type, string>(t => (t.Namespace ?? string.Empty), StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase);
}

但是此时还不是最终的缓存类型,上述已经说过会通过 DefaultHttpControllerSelector 中的方法 InitializeControllerInfoCache 遍历该控制器类型缓存所生成的类型为  Lazy<Dictionary<string, ILookup<string, Type>>>  而得到类型为  Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>> 。此上就是整个生成控制器缓存的过程。

接下来就是进入最后一个处理程序 HttpControllerDispatcher 中的方法 SendAsyncInternal 方法的第二步,在选择请求中的对应的控制器以及进行控制器缓存后并返回HttpControllerDescriptor来创建控制器以及激活控制器。请继续往下看。

HttpControllerDescriptor 

第二步:IHttpController controller = descriptor.CreateController(request)

我们看看此类的定义:

 1 public class HttpControllerDescriptor
 2 {
 3     // Fields
 4     private object[] _attrCached;
 5     private HttpConfiguration _configuration;
 6     private string _controllerName;
 7     private Type _controllerType;
 8     private readonly ConcurrentDictionary<object, object> _properties;
 9
10     // Methods
11     public HttpControllerDescriptor();
12     internal HttpControllerDescriptor(HttpConfiguration configuration);
13     public HttpControllerDescriptor(HttpConfiguration configuration, string controllerName, Type controllerType);
14     public virtual IHttpController CreateController(HttpRequestMessage request);
15     public virtual Collection<T> GetCustomAttributes<T>() where T: class;
16     public virtual Collection<IFilter> GetFilters();
17     private void Initialize();
18     internal void Initialize(HttpConfiguration configuration);
19     private static void InvokeAttributesOnControllerType(HttpControllerDescriptor controllerDescriptor, Type type);
20
21     // Properties
22     public HttpConfiguration Configuration { get; set; }
23     public string ControllerName { get; set; }
24     public Type ControllerType { get; set; }
25     public virtual ConcurrentDictionary<object, object> Properties { get; }
26 }

此类最重要的当属上述红色标记的CreateController方法了,我们查看其定义:

public virtual IHttpController CreateController(HttpRequestMessage request)
{
    if (request == null)
    {
        throw Error.ArgumentNull("request");
    }
    return this.Configuration.Services.GetHttpControllerActivator().Create(request, this, this.ControllerType);
}

接下来调用GetHttpControllerActivator方法:

public static IHttpControllerActivator GetHttpControllerActivator(this ServicesContainer services)
{
    return services.GetServiceOrThrow<IHttpControllerActivator>();
}

而注册实现IHttpControllerActivator接口,则依然是通过DefaultServices来实现,如下:

    this.SetSingle<IHttpControllerActivator>(new DefaultHttpControllerActivator());

DefaultHttpControllerActivator负责激活控制器,那么是怎么样激活控制器的呢?接下来看看DefaultHttpControllerActivator类的定义:

 1 public class DefaultHttpControllerActivator : IHttpControllerActivator
 2 {
 3     // Fields
 4     private object _cacheKey;
 5     private Tuple<HttpControllerDescriptor, Func<IHttpController>> _fastCache;
 6
 7     // Methods
 8     public DefaultHttpControllerActivator();
 9     public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType);
10     private static IHttpController GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, out Func<IHttpController> activator);
11 }

最重要的是上述GetInstanceOrActivator方法来生成并激活IHttpController,查看此方法的实现如下:

private static IHttpController GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, out Func<IHttpController> activator)
{
    IHttpController service = (IHttpController) request.GetDependencyScope().GetService(controllerType);
    if (service != null)
    {
        activator = null;
        return service;
    }
    activator = TypeActivator.Create<IHttpController>(controllerType);
    return null;
}

由上知,此时DefaultHttpControllerActivator会从HttpConfiguration中获取DependencyResolver属性对应的容器,若此时容器为空,并通过反射调用TypeActivator来生成并激活控制器。以上就是整个创建控制器的整个过程。接下来我们继续来看文章开头  SendAsyncInternal 方法的第三步,请继续往下看!

HttpControllerContext

第三步:HttpControllerContext controllerContext = new HttpControllerContext(......)  

由此句知,利用构造函数将返回值为IHttpController的Controller属性以及返回值为HttpControllerDescriptor的属性ControllerDescriptor进行赋值,而得到一个有关控制器的上下文,下面我们来看看此类的定义:

public class HttpControllerContext
{
    // Fields
    private HttpConfiguration _configuration;
    private IHttpController _controller;
    private HttpControllerDescriptor _controllerDescriptor;
    private HttpRequestMessage _request;
    private IHttpRouteData _routeData;

    // Methods
    public HttpControllerContext();
    public HttpControllerContext(HttpConfiguration configuration, IHttpRouteData routeData, HttpRequestMessage request);

    // Properties
    public HttpConfiguration Configuration { get; set; }
    public IHttpController Controller { get; set; }
    public HttpControllerDescriptor ControllerDescriptor { get; set; }
    public HttpRequestMessage Request { get; set; }
    public IHttpRouteData RouteData { get; set; }
}

所谓的控制器上下文就是一个HttpControllerContext对象对应一个控制器的HttpContext,我们可以通过上述Controller属性类设置这个HttpControllerContext对象,不仅如此,我们还可以通过上述ControllerDescriptor属性来设置控制器的HttpControllerDescriptor对象。

 

我们通过上述创建了控制器上下文HttpControllerContext对象,并最终执行SendAsyncInternal方法的最后一句

return controller.ExecuteAsync(controllerContext, cancellationToken);

通过调用控制器上的ExecuteAsync方法将当前获得的控制器上下文传递进去最终做出响应。(这就涉及到控制器的执行过程下一节讲控制器的执行过程)  

总结

(1)简单回顾下控制器的创建的过程

由隶属于HttpRoutingDispatcher类型的HttpControllerDispatcher中的一个返回值为IHttpControllerSelector的属性ControllerSelector,这个ControllerSelector就是DefaultHttpControllerSelector,并调用DefaultHttpControllerSelector中的SelectController()方法,然后由此方法返回的HttpControllerDescriptor的类型变量descriptor,最终调用此变量中的CreateController来创建并激活控制器。

(2)关于控制器创建的详细示意图如下:(来源:控制器创建示意图

 

 

时间: 2024-09-20 20:51:57

Web APi之控制器创建过程及原理解析(八)的相关文章

Web APi之过滤器创建过程原理解析【一】(十)

前言 Web API的简单流程就是从请求到执行到Action并最终作出响应,但是在这个过程有一把[筛子],那就是过滤器Filter,在从请求到Action这整个流程中使用Filter来进行相应的处理从而作出响应,这对于认证以及授权等是及其重要的,所以说过滤器应用是Web API框架中非常重要的一种实现方式,我们有必要去探讨其原理. 过滤器及其提供机制 Web API框架提供了一个请求.响应的消息处理管道,并且其框架极具扩展性,通过其扩展可以对执行的流程进行适当的干预,其扩展点就体现在以下三个方面

Web APi之控制器选择Action方法过程(九)

前言 前面我们叙述了关于控制器创建的详细过程,在前面完成了对控制器的激活之后,就是根据控制器信息来查找匹配的Action方法,这就是本节要讲的内容.当请求过来时首先经过宿主处理管道然后进入Web API消息处理管道,接着就是控制器的创建和执行控制器即选择匹配的Action方法最终并作出响应(在Action方法上还涉及到模型绑定.过滤器等,后续讲).从上知,这一系列大部分内容都已囊括,我们这一系列也就算快完事,在后面将根据这些管道事件以及相关的处理给出相应的练习来熟练掌握,希望此系列能帮助到想学习

Web APi之过滤器执行过程原理解析【二】(十一)

前言 上一节我们详细讲解了过滤器的创建过程以及粗略的介绍了五种过滤器,用此五种过滤器对实现对执行Action方法各个时期的拦截非常重要.这一节我们简单将讲述在Action方法上.控制器上.全局上以及授权上的自定义特性的执行过程. APiController  之前有讲到该APiController,也就稍微介绍了,这节我们来详细此Web API控制器的基类: 1 public abstract class ApiController : IHttpController, IDisposable

Web APi入门之Self-Host寄宿及路由原理(二)

前言 刚开始表面上感觉Web API内容似乎没什么,也就是返回JSON数据,事实上远非我所想,不去研究不知道,其中的水还是比较深,那又如何,一步一个脚印来学习都将迎刃而解. Self-Host 我们知道Web API它可以快速为HTTP客户端提供API来创建Web服务,为何如此这样说呢?因为我们可以将其作为主机也就是一个服务器来用完全不需要IIS,这就是我们下面要讲的第一个内容Self-Host,实现对Web API寄宿的方式有多种并且都是独立于ASP.NET框架之外,如下Self-Host寄宿

ASP.NET Web API的Controller是如何被创建的?

Web API调用请求的目标是定义在某个HttpController类型中的某个Action方法,所以消息处理管道最终需要激活目标HttpController对象.调用请求的URI会携带目标HttpController的名称,该名称经过路由解析之后会作为路由变量保存到一个HttpRouteData对象中,而后者会被添加到代表当前请求的HttpRequestMessage对象的属性字典中.ASP.NET Web API据此解析出目标HttpController的类型,进而实现针对目标HttpCon

【ASP.NET Web API教程】2.4 创建Web API的帮助页面

原文:[ASP.NET Web API教程]2.4 创建Web API的帮助页面 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. 2.4 Creating a Help Page for a Web API 2.4 创建Web API帮助页面 本文引自:http://www.asp.net/web-api/overview/creating-web-apis/creating-a-help-page-for-a-web-api By

使用 Swagger UI 与 Swashbuckle 创建 RESTful Web API 帮助文件

作者:Sreekanth Mothukuru 2016年2月18日 本文旨在介绍如何使用常用的 Swagger 和 Swashbuckle 框架创建描述 Restful API 的交互界面,并为 API 用户提供丰富的探索.文件和操作体验. 源代码: 下载 SwaggerUi_2.zip 步骤 在本文中,我们将在 Asp.Net 创建一个简单的 Restful API,并整合 Swashbuckle 和 Swagger UI.本文分为三部分. 创建 Asp.Net Web API项目 通过实体数

Asp.Net Web API 2第一课——入门

原文:Asp.Net Web API 2第一课--入门 前言 Http不仅仅服务于Web Pages.它也是一个创建展示服务和数据的API的强大平台.Http是简单的.灵活的.无处不在的.你能想象到几乎任何的平台都会有HTTP服务库.HTTP服务可以涉及到范围广泛的客户端,包括浏览器.各种移动设备和传统的桌面应用程序. Asp.Net Web API是在.NET Framework框架上用于建立Web APIs的一个框架.在这个教程中,你将会使用Asp.Net Web API框架来创建一个能够返

Web API应用架构在Winform混合框架中的应用(1)

在<Web API应用架构设计分析(1)>和<Web API应用架构设计分析(2)>中对WebAPI的架构进行了一定的剖析,在当今移动优先的口号下,传统平台都纷纷开发了属于自己的Web API平台,方便各种终端系统的接入,很多企业的需求都是以Web API优先的理念来设计整个企业应用体系的.Web API作为整个纽带的核心,在整个核心层需要考虑到统一性.稳定性.以及安全性等方面因素.本文主要介绍,Web API应用架构,在Winform整合中的角色,以及如何实现在Winform混合