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

时间:2022-10-27 15:37:20

前言

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

HttpControllerDispatcher

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

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

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

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

通过上面我们看到了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是通过此类而实现,所以我们再来看看此类中关于此方法的定义及实现:

 public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
HttpControllerDescriptor descriptor;
string controllerName = this.GetControllerName(request);
if (this._controllerInfoCache.Value.TryGetValue(controllerName, out descriptor))
{
return descriptor;
}
ICollection<Type> controllerTypes = this._controllerTypeCache.GetControllerTypes(controllerName);
}

我们首先看看是红色标记第一个,它是如何获得控制器名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,所以还是仔细看看该类定义:

 public class DefaultHttpControllerSelector : IHttpControllerSelector
{
// Fields
private readonly HttpConfiguration _configuration;
private readonly Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>> _controllerInfoCache;
private readonly HttpControllerTypeCache _controllerTypeCache;
private const string ControllerKey = "controller";
public static readonly string ControllerSuffix; // Methods
static DefaultHttpControllerSelector();
public DefaultHttpControllerSelector(HttpConfiguration configuration);
private static Exception CreateAmbiguousControllerException(IHttpRoute route, string controllerName, ICollection<Type> matchingTypes);
public virtual IDictionary<string, HttpControllerDescriptor> GetControllerMapping();
public virtual string GetControllerName(HttpRequestMessage request);
private ConcurrentDictionary<string, HttpControllerDescriptor> InitializeControllerInfoCache();
public virtual HttpControllerDescriptor SelectController(HttpRequestMessage request);
}

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

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

  • 生成控制器缓存  

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

 public DefaultHttpControllerSelector(HttpConfiguration configuration)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
this._controllerInfoCache = new Lazy<ConcurrentDictionary<string, HttpControllerDescriptor>>(new Func<ConcurrentDictionary<string, HttpControllerDescriptor>>(this.InitializeControllerInfoCache));
this._configuration = configuration;
this._controllerTypeCache = new HttpControllerTypeCache(this._configuration);
}

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

 private ConcurrentDictionary<string, HttpControllerDescriptor> InitializeControllerInfoCache()
{
ConcurrentDictionary<string, HttpControllerDescriptor> dictionary = new ConcurrentDictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
HashSet<string> set = new HashSet<string>();
foreach (KeyValuePair<string, ILookup<string, Type>> pair in this._controllerTypeCache.Cache)
{
string key = pair.Key;
foreach (IGrouping<string, Type> grouping in pair.Value)
{
foreach (Type type in grouping)
{
if (dictionary.Keys.Contains(key))
{
set.Add(key);
break;
}
dictionary.TryAdd(key, new HttpControllerDescriptor(this._configuration, key, type));
}
}
}
foreach (string str2 in set)
{
HttpControllerDescriptor descriptor;
dictionary.TryRemove(str2, out descriptor);
}
return dictionary;
}

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

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

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

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

再来看看此类的定义:

 public class DefaultHttpControllerTypeResolver : IHttpControllerTypeResolver
{
// Fields
private readonly Predicate<Type> _isControllerTypePredicate; // Methods
public DefaultHttpControllerTypeResolver();
public DefaultHttpControllerTypeResolver(Predicate<Type> predicate);
public virtual ICollection<Type> GetControllerTypes(IAssembliesResolver assembliesResolver);
internal static bool IsControllerType(Type t); // Properties
protected Predicate<Type> IsControllerTypePredicate { get; }
}

通过上述方法根据过滤条件等来加载指定程序集中所有符合条件的控制其类型(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(, 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)

我们看看此类的定义:

 public class HttpControllerDescriptor
{
// Fields
private object[] _attrCached;
private HttpConfiguration _configuration;
private string _controllerName;
private Type _controllerType;
private readonly ConcurrentDictionary<object, object> _properties; // Methods
public HttpControllerDescriptor();
internal HttpControllerDescriptor(HttpConfiguration configuration);
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<IFilter> GetFilters();
private void Initialize();
internal void Initialize(HttpConfiguration configuration);
private static void InvokeAttributesOnControllerType(HttpControllerDescriptor controllerDescriptor, Type type); // Properties
public HttpConfiguration Configuration { get; set; }
public string ControllerName { get; set; }
public Type ControllerType { get; set; }
public virtual ConcurrentDictionary<object, object> Properties { get; }
}

此类最重要的当属上述红色标记的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类的定义:

 public class DefaultHttpControllerActivator : IHttpControllerActivator
{
// Fields
private object _cacheKey;
private Tuple<HttpControllerDescriptor, Func<IHttpController>> _fastCache; // Methods
public DefaultHttpControllerActivator();
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType);
private static IHttpController GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, out Func<IHttpController> activator);
}

最重要的是上述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)关于控制器创建的详细示意图如下:(来源:控制器创建示意图

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