ASP.NET Web API Selfhost宿主环境中管道、路由
首先我们先来看个示意图,大概的描述了在SelfHost宿主环境中管道形态。
图1
因为在WebHost宿主环境中ASP.NET Web API的管道请求接收以及响应的返回最后都是由ASP.NET来包办的(下一篇中讲解),而在SelfHost宿主环境中就苦逼了,没有那么简单了。
我们按照图1中示意的来讲解,首先在SelfHost宿主环境中的项目启动之后(当然项目要使用Web API框架的),会有一个HttpBinding对象(System.Web.Http.SelfHost.Channels),那这个 HttpBinding类型的对象是干嘛的呢?Httpbinding对象对应着一些个BindingElement对象,而这些 BindingElement又各自生成对应的管道层监听器,这样就如图1中所示的那样,现在我们看一下如下的示例代码,看看HttpBinding到底 对应着哪些BindingElement对象。
示例代码1-1
public class HttpBinding : Binding, IBindingRuntimePreferences { public HttpBinding() { this.Initialize(); } private void Initialize() { this._security = new HttpBindingSecurity(); this._httpTransportBindingElement = new HttpTransportBindingElement(); this._httpTransportBindingElement.ManualAddressing = true; this._httpsTransportBindingElement = new HttpsTransportBindingElement(); this._httpsTransportBindingElement.ManualAddressing = true; this._httpMessageEncodingBindingElement = new HttpMessageEncodingBindingElement(); } }
在示例代码1-1中我们可以清楚的看到在HttpBinding对象的构造函数中分别的对几种BindingElement进行了实例化赋值,我们 只对其中的HttpTransportBindingElement和HttpMessageEncodingBindingElement进行讲解也就 是图1中所示的那样。
HttpTransportBindingElement对象的主要职责就是生成相对应的管道监听器,这里对应的就是 IChannelListener<TChannel>泛型类型了,而生成好对应的管道监听器之后,监听器之后会开始监听与之对应的管道层, 与HttpTransportBindingElement对象以及监听器对应的也就是TransprotChannel管道层了,它负责请求消息的接收 和响应消息的发送。
HttpMessageEncodingBindingElement类型的对象所做操作同 HttpTransportBindingElement类型一致,都是先要生成对应的管道监听器,在这里与之对应的就是 HttpMessageEncodingChannelListener类型,在监听器生成好之后也会去监听对应的EncodingChannel管道 层,而EncodingChannel管道层主要的作用就是将请求信息封装为HttpMessage类型的消息对象,之后由HttpMessage消息对 象进入ASP.NET Web API框架的管道系统中。
上面说的是在请求未到达ASP.NET Web API框架的管道系统中的时候在外部的一些处理和操作,下面我们就要说明一下内部,在上篇的《ASP.NET Web API 管道模型》篇幅中有示例代码演示过在SelfHost环境下管道的注册,我们这里看一下在SelfHost环境中Web API框架自身的管道系统里的对象的一些类型。
HttpSelfHostServer消息处理程序(实现类-管道头)System.Web.Http.SelfHost
public sealed class HttpSelfHostServer : HttpServer { public HttpSelfHostServer(HttpSelfHostConfiguration configuration); public HttpSelfHostServer(HttpSelfHostConfiguration configuration, HttpMessageHandler dispatcher); public Task CloseAsync(); protected override void Dispose(bool disposing); public Task OpenAsync(); }
可以看到HttpSelfHostServer类型继承自HttpServer,在上篇中我们也就提到过HttpServer是继承自 DelegatingHandler抽象类型的消息处理程序基类,DelegatingHandler与HttpMessageHandler的不同之处 就是多了个指向下一个处理程序的引用,当然了作为一个管道系统中第一个消息处理程序必须是要有指向下一个处理程序引用的这么一个标识,这样是合理的。我们 再看HttpSelfHostServer类型的构造函数的参数类型。
HttpSelfHostConfiguration类型是继承自HttpConfiguration类型的,在上篇中我们也说 过,HttpConfiguration中可以配置管道中的大多数信息,这个大家可以自己去看一下。在重载构造函数中有了第二个构造函数参 数,HttpMessageHandler类型的参数,在默认使用HttpSelfHostServer的时候假使不使用这个重载的构造函数,那么在 HttpSelfHostServer实例化的之前先实例化之前,其基类HttpServer的构造函数开始执行,所以在看下HttpServer类型的 构造函数的时候我们可以看到这里默认设置的HttpMessageHandler类型的参数到底是什么样子的。
public HttpServer() : this(new HttpConfiguration()) { } public HttpServer(HttpMessageHandler dispatcher) : this(new HttpConfiguration(), dispatcher) { } public HttpServer(HttpConfiguration configuration) : this(configuration, new HttpRoutingDispatcher(configuration)) { } public HttpServer(HttpConfiguration configuration, HttpMessageHandler dispatcher) { this._initializationLock = new object(); if (configuration == null) { throw System.Web.Http.Error.ArgumentNull("configuration"); } if (dispatcher == null) { throw System.Web.Http.Error.ArgumentNull("dispatcher"); } this._dispatcher = dispatcher; this._configuration = configuration; }
这里大家可以清楚的看到是HttpRoutingDispatcher类型作为管道的最后一个处理程序的类型,对于这个类型以及详细的信息,在下面的路由小节中会有说明。
ASP.NET Web API SelfHost宿主环境路由
对于路由的其他知识这里就不说了,就是简要的提一下在SelfHost中路由、管道的一些细节。
图2
这里要详细说明的就是HttpRoutingDispatcher类型中的SendAsync()方法,看下源码中的实现这样更清楚。
public class HttpRoutingDispatcher : HttpMessageHandler { // Fields private readonly HttpConfiguration _configuration; private readonly HttpMessageInvoker _defaultInvoker; // Methods public HttpRoutingDispatcher(HttpConfiguration configuration) : this(configuration, new HttpControllerDispatcher(configuration)) { } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { IHttpRouteData routeData; if (!request.Properties.TryGetValue<IHttpRouteData>(HttpPropertyKeys.HttpRouteDataKey, out routeData)) { routeData = this._configuration.Routes.GetRouteData(request); if (routeData == null) { return TaskHelpers.FromResult<HttpResponseMessage>(request.CreateErrorResponse(HttpStatusCode.NotFound, Error.Format(SRResources.ResourceNotFound, new object[] { request.RequestUri }), SRResources.NoRouteData)); } request.Properties.Add(HttpPropertyKeys.HttpRouteDataKey, routeData); } RemoveOptionalRoutingParameters(routeData.Values); HttpMessageInvoker invoker = (routeData.Route.Handler == null) ? this._defaultInvoker : new HttpMessageInvoker(routeData.Route.Handler, false); return invoker.SendAsync(request, cancellationToken); } }
我们先看一下HttpRoutingDispatcher类型中构造函数,可以看到在定义的构造函数后面紧接着又在实例基类的构造函数,并且第二个 HttpMessageHandler类型的参数为HttpControllerDispatcher实例的对象,这个我们先记住就行了。
下面我们还是回到HttpRoutingDispatcher类型的SendAsync()方法中,首先我们会看到从HttpRequestMessage对象实例的Properties属性集合中获取路由数据对象。
在SelfHost的环境下这是获取不到的,为啥?因为上面以及之前的篇幅中在管道的处理中没有提到过处理路由并且生成路由数据的。所以这个时候会 根据HttpConfiguration中的HttpRouteCollection类型的属性Routes,Routes属性再根据 SendAsync()方法的参数类型为HttpRequestMessage的request信息获取路由数据对象IHttpRouteData。
在匹配成功获取到路由数据对象(IHttpRouteData)之后便会添加至HttpRequestMessage对象实例(request)的Properties属性集合中。
之前对于路由的了解,最后的执行的Handler都是起初定义在路由对象中的,而在实际情况中,我们注册路由的时候并没有,假使这种情况就在现在发 生,可以看到routeData.Route.Hander==null这个是成立的,所以执行的是我们先前说过的在构造函数中的 HttpControllerDispatcher类型的实例的SendAsync()方法(实际当中HttpControllerDispatcher 类型被HttpMessageInvoker类型所封装)。
关键字:ASP.NET、管道、路由、
新文章:
- CentOS7下图形配置网络的方法
- CentOS 7如何添加删除用户
- 如何解决centos7双系统后丢失windows启动项
- CentOS单网卡如何批量添加不同IP段
- CentOS下iconv命令的介绍
- Centos7 SSH密钥登陆及密码密钥双重验证详解
- CentOS 7.1添加删除用户的方法
- CentOS查找/扫描局域网打印机IP讲解
- CentOS7使用hostapd实现无AP模式的详解
- su命令不能切换root的解决方法
- 解决VMware下CentOS7网络重启出错
- 解决Centos7双系统后丢失windows启动项
- CentOS下如何避免文件覆盖
- CentOS7和CentOS6系统有什么不同呢
- Centos 6.6默认iptable规则详解