ASP.NET Web API消息处理管道:Self Host下的消息处理管道[下篇]
ASP.NET Web API消息处理管道:Self Host下的消息处理管道[下篇]
我们知道ASP.NET Web API借助于HttpSelfHostServer以Self Host模式寄宿于当前应用程序中。而通过《上篇》的介绍我们知道Self Host下请求的监听、接收和响应是利用HttpBinding实现的,那么HttpSelfHostServer与上面介绍的HttpBinding又有何关系?HttpSelfHostServer与ASP.NET Web API的消息处理管道又是如何集成的呢?[本文已经同步到《How ASP.NET Web API Works?》]
如下面的代码片断所示,HttpSelfHostServer其实是HttpServer的子类,所以在Self Host模式下HttpSelfHostServer本身就是消息管道的组成部分。换句行话说,以HttpSelfHostServer为“龙头”的消息处理管道本身具有了请求监听、接收、处理和响应的能力。
public sealed class HttpSelfHostServer : HttpServer
{
public HttpSelfHostServer(HttpSelfHostConfiguration configuration);
public HttpSelfHostServer(HttpSelfHostConfiguration configuration, HttpMessageHandler dispatcher); public Task OpenAsync();
public Task CloseAsync(); protected override void Dispose(bool disposing);
}
Self Host模式下的消息处理管道通过创建HttpSelfHostServer时指定的HttpSelfHostConfiguration对象进行配置,其中就包括监听监听的地址。
HttpSelfHostConfiguration
如下面的代码片断所示,HttpSelfHostConfiguration直接继承自HttpConfiguration。我们在创建一个HttpSelfHostConfiguration对象的时候需要指定一个Uri对象作为监听基地址,这个地址通过只读属性BaseAddress返回。
public class HttpSelfHostConfiguration : HttpConfiguration
{
public HttpSelfHostConfiguration(Uri baseAddress); public Uri BaseAddress { get; }
public HostNameComparisonMode HostNameComparisonMode { get; set; }
public TransferMode TransferMode { get; set; } public int MaxBufferSize { get; set; }
public int MaxConcurrentRequests { get; set; }
public long MaxReceivedMessageSize { get; set; } public TimeSpan ReceiveTimeout { get; set; }
public TimeSpan SendTimeout { get; set; } public HttpClientCredentialType ClientCredentialType { get; set; }
public UserNamePasswordValidator UserNamePasswordValidator { get; set; }
public X509CertificateValidator X509CertificateValidator { get; set; }
}
由于Self Host模式下请求的监听、接收和响应基本上全部是通过HttpBinding实现的,所以定义在HttpSelfHostConfiguration中的众多属性实际上基本用于对创建的HttpBinding进行相应配置。从如下给出的代码片断可以看出HttpBinding类型与HttpSelfHostConfiguration具有类似的属性定义。
public abstract class Binding : IDefaultCommunicationTimeouts
{
//其他成员
public TimeSpan ReceiveTimeout { get; set; }
public TimeSpan SendTimeout { get; set; }
} public class HttpBinding : Binding, IBindingRuntimePreferences
{
//其他成员
public HostNameComparisonMode HostNameComparisonMode { get; set; }
public TransferMode TransferMode { get; set; } public long MaxBufferPoolSize { get; set; }
public int MaxBufferSize { get; set; }
public long MaxReceivedMessageSize { get; set; } public HttpBindingSecurity Security { get; set; }
}
由于Binding在WCF是一个核心组件,其设计本身相对复杂,要深入了解定义在HttpBinding中的这些属性需要相关的背景知识。篇幅所限,我们不能因为这些属性将Binding相关的内容全部搬过来,所以在这里我们仅仅通过如下的表格对它们进行概括性的介绍。
|
属性 |
描述 |
|
HostNameComparisonMode |
如果请求URL没有指定服务器的IP地址而是主机名称,当从URL提取主机名称后会按照相应的比较模式来最终确定匹配的主机名。该属性的类型为HostNameComparisonMode枚举,用以确定在主机名比较模式。 |
|
TransferMode |
消息传输具有两种模式,即基于Streamed和Buffered,前者以流的形式进行消息传输,后者则将整个消息的内容先保存于内存缓冲区后一并传输。该属性类型为TransferMode枚举,用以控制针对请求消息和响应消息的传输模式。在默认情况下,请求消息和响应消息均以Buffered模式进行传输。MaxBufferSize在采用Buffered模式下消息最大缓冲大小,默认值为65536(0x10000)。 |
|
MaxConcurrentRequests |
这两个是针对请求的限制,分别表示运行的最大并发访问量和请求消息允许的最大尺寸。两者的默认值分别为100和65536(0x10000)。值得一提的是:MaxConcurrentRequests针对最大并发请求的限制是针对单个处理器设定的,对于多处理器或者多核处理来说,应该乘以处理器的数量。 |
|
ReceiveTimeout |
这两个属性分别表示接收请求消息和发送响应消息的超时时限,默认值分别为10分钟和1分钟。 |
|
ClientCredentialType |
这是三个与安全认证相关的属性。ClientCredentialType表示客户端采用的用户凭证类型,而UserNamePasswordValidator和X509CertificateValidator属性值分别在用户凭证为“用户名+密码”和“X.509证书”的情况下实施认证。 |
HttpSelfHostServer与消息处理管道
在对定义的Web API采用Self Host模式寄宿时,我们会根据指定的监听基地址创建一个HttpSelfHostConfiguration对象,然后据此创建一个HttpSelfHostServer对象。当我们调用方法OpenAsync时,HttpSelfHostServer会创建一个HttpBinding对象,并根据指定的HttpSelfHostConfiguration对其作相应的设置。随后HttpBinding会针对指定的监听地址创建一个ChannelListener,并调用其BeginOpen方法以异步的方式开启。除了OpenAsync方法外,HttpSelfHostServer还定义了一个CloseAync方法使我们可以以异步的方式关闭已经开启的ChannelListener。

一旦ChannelListener被成功开启后,它便绑定到指定的监听地址进行请求的监听。一旦抵达的请求被探测到,它会利用创建的信道栈来接收该请求。通过上面对HttpBinding的介绍我们知道,接收到的二进制数据经过解码之后会生成一个HttpMessage对象,后者是对一个HttpRequestMessage的封装。
接下来HttpSelfHostServer从生成的HttpMessage中提取被封装的HttpRequestMessage对象,直接分发给消息处理管道作进一步处理。对于最终从管道传递回来的HttpResponseMessage对象,HttpSelfHostServer将其封装成一个HttpMessage对象并利用创建的信道栈返回给客户端。在通过传输层发送响应消息之前,HttpMessage会先编码。通过上面的介绍我们知道整个编码工作完全是针对被HttpMessage封装的HttpResponseMessage对象进行的,在HttpResponseMessage中保存的响应内容就是客户端接收到的内容。左图基本揭示了Self Host寄宿模式下整个消息处理管道的结构。
实例演示:创建自定义HttpServer模拟HttpSelfHostServer的工作原理
通过上面的介绍,我想读者朋友们应该对Self Host模式下消息处理管道如何进行请求的监听、接收、处理和响应已经有了全面的了解。如果我们能够创建一个自定义的HttpServer来模拟HttpSelfHostServer的工作原理,我想读者朋友们对此的印象一定更加深刻。在本章内容即将完结之前,我们就来完成这么一个演示实例。
我们通过继承HttpServer创建如下一个用于模拟HttpSelfHostServer的MyHttpSelfHostServer类型。两者对于请求的监听、接收和响应的实现原理是一致的,不同之处在于HttpSelfHostServer基本采用异步的操作方式,处于简单简洁考虑,MyHttpSelfHostServer采用同步编程方式。
public class MyHttpSelfHostServer: HttpServer
{
public Uri BaseAddress { get; private set; }
public IChannelListener<IReplyChannel> ChannelListener { get; private set; } public MyHttpSelfHostServer(HttpConfiguration configuration, Uri baseAddress) : base(configuration)
{
this.BaseAddress = baseAddress;
} public void Open()
{
HttpBinding binding = new HttpBinding();
this.ChannelListener = binding.BuildChannelListener<IReplyChannel>(this.BaseAddress);
this.ChannelListener.Open(); IReplyChannel channnel = this.ChannelListener.AcceptChannel();
channnel.Open(); while (true)
{
RequestContext requestContext = channnel.ReceiveRequest(TimeSpan.MaxValue);
Message message = requestContext.RequestMessage;
MethodInfo method = message.GetType().GetMethod("GetHttpRequestMessage");
HttpRequestMessage request = (HttpRequestMessage)method.Invoke(message, new object[] {true});
Task<HttpResponseMessage> processResponse = base.SendAsync(request, new CancellationTokenSource().Token);
processResponse.ContinueWith(task =>
{
string httpMessageTypeName = "System.Web.Http.SelfHost.Channels.HttpMessage, System.Web.Http.SelfHost";
Type httpMessageType = Type.GetType(httpMessageTypeName);
Message reply = (Message)Activator.CreateInstance( httpMessageType, new object[] { task.Result });
requestContext.Reply(reply);
});
}
} public void Close()
{
if (null != this.ChannelListener && this.ChannelListener.State == CommunicationState.Opened)
{
this.ChannelListener.Close();
}
}
}
MyHttpSelfHostServer的只读属性BaseAddress表示监听基地址,该属性直接在构造函数中指定。与HttpSelfHostServer不同的是,用于创建MyHttpSelfHostServer提供的配置对象是一个HttpConfiguration对象而不再是HttpSelfHostConfiguration。
在Open方法中,我们根据提供的监听基地址利用HttpBinding对象创建一个ChannelListener对象,MyHttpSelfHostServer的只读属性ChannelListener引用的也正是这个对象。在开启该ChannelListener之后,我们调用其AccpetChannel方法创建信道栈,最终返回位于栈顶的Channel。在该Channel开启的情况下,我们在一个“永不终止”的While循环中调用其ReceiveRequest方法进行请求的监听。
当信道栈成功接收请求消息后(通过前面的介绍我们知道这是一个HttpMessage对象),我们从中提取出被封装的HttpRequestMessage对象,并将其作为参数调用SendAsync方法,表示请求的HttpReuqestMessage自此进入了消息处理管道这个流水车间。
调用SendAsync方法返回的是一个Task<HttpResponseMessage>对象,我们对此作这样的后续操作:提供作为响应消息的HttpResponseMessage对象,并通过反射的形式将其封装成HttpMessage对象,最终将此对象通过信道栈对请求予以最终的响应。
HttpSelfHostServer定义了OpenAsync和CloseAsync方法开启和关闭监听器,与之相匹配,我们也为Open方法定义了匹配的Close方法来关闭已经开启的ChannelListener。
为了验证我们自定义的MyHttpSelfHostServer是否能够替代“原生”的HttpSelfHostServer,我们在一个控制台中定义了如下一个ContactsController。它具有两个重载的Action方法Get,前者用于返回所有的联系人列表,后者返回指定ID的某个联系人信息。
public class ContactsController : ApiController
{
private static List<Contact> contacts = new List<Contact>
{
new Contact{ Id="001", Name = "张三", PhoneNo="123", EmailAddress="zhangsan@gmail.com"},
new Contact{ Id="002", Name = "李四", PhoneNo="456", EmailAddress="lisi@gmail.com"}
}; public IEnumerable<Contact> Get()
{
return contacts;
} public Contact Get(string id)
{
return contacts.FirstOrDefault(c => c.Id == id);
}
} public class Contact
{
public string Id { get; set; }
public string Name { get; set; }
public string PhoneNo { get; set; }
public string EmailAddress { get; set; }
}
在作为入口的Main方法中我们编写了如下一段简单的“寄宿”程序。我们根据创建的HttpConfiguration对象和指定的监听基地址(“http://127.0.0.1:3721”)创建了一个MyHttpSelfHostServer对象。在调用Open方法开始监听之前,我们注册了一个URL模板为“http://127.0.0.1:3721”的HttpRoute。
class Program
{
static void Main(string[] args)
{
using (MyHttpSelfHostServer httpServer = new MyHttpSelfHostServer( new HttpConfiguration(), new Uri("http://127.0.0.1:3721")))
{
httpServer.Configuration.Routes.MapHttpRoute(
name : "DefaultApi",
routeTemplate : "api/{controller}/{id}",
defaults : new { id = RouteParameter.Optional }); httpServer.Open();
Console.Read();
}
}
}

运行该程序之后,这个“宿主”程序便开始进行请求的监听。现在我们直接利用浏览器对定义在ContactsController中的两个Action方法Get发起请求,通过注册的HttpRoute和请求的HTTP方法直接作为Action名称的原理,我们使用的URL分别为“http://127.0.0.1:3721/api/contacts”和“http://127.0.0.1:3721/api/contacts/001”。如右图所示,我们期望的联系人信息直接以XML的形式显示在浏览器中,由此可见我们自定义的MyHttpSelfHostServer“不辱使命”。
ASP.NET Web API消息处理管道:Self Host下的消息处理管道[下篇]的更多相关文章
- ASP.NET Web API的消息处理管道: Self Host下的消息处理管道[上篇]
ASP.NET Web API的消息处理管道: Self Host下的消息处理管道[上篇] ASP.NET Web API服务端框架核心是一个独立于具体寄宿环境的消息处理管道,它不关心请求消息来源于何 ...
- ASP.NET Web API是如何根据请求选择Action的?[下篇]
ASP.NET Web API是如何根据请求选择Action的?[下篇] 再<上篇>中我们简单介绍了用于实现Action选择机制的HttpActionSelector,接下来我们来讨论本章 ...
- ASP.NET Web API 框架研究 Self Host模式下的消息处理管道
Self Host模式下的ASP.NET Web API与WCF非常相似,都可以寄宿在任意类型的托管应用程序中,宿主可以是Windows Form .WPF.控制台应用以及Windows Servic ...
- ASP.NET Web API是如何根据请求选择Action的?[下篇] 【转】
再<上篇>中我们简单介绍了用于实现Action选择机制的HttpActionSelector,接下来我们来讨论本章最为核心的内 容:ASP.NET Web API如何利用HttpActio ...
- ASP.NET Web API 框架研究 Controller创建过程与消息处理管道
现在我们从代码角度来看下,从消息处理管道末尾是怎么创建出Controller实例的.消息处理管道末端是一个叫HttpRoutingDispatcher的处理器,其内部完成路由后 ,会把消息派送给其内部 ...
- 细说Asp.Net Web API消息处理管道(二)
在细说Asp.Net Web API消息处理管道这篇文章中,通过翻看源码和实例验证的方式,我们知道了Asp.Net Web API消息处理管道的组成类型以及Asp.Net Web API是如何创建消息 ...
- ASP.NET Web API标准的“管道式”设计
ASP.NET Web API的核心框架是一个消息处理管道,这个管道是一组HttpMessageHandler的有序组合.这是一个双工管道,请求消息从一端流入并依次经过所有HttpMessageHan ...
- Self Host模式下的ASP. NET Web API是如何进行请求的监听与处理的?
构成ASP.NET Web API核心框架的消息处理管道既不关心请求消息来源于何处,也不需要考虑响应消息归于何方.当我们采用Web Host模式将一个ASP.NET应用作为目标Web API的宿主时, ...
- ASP.NET Web API的安全管道
本篇体验ASP.NET Web API的安全管道.这里的安全管道是指在请求和响应过程中所经历的各个组件或进程,比如有IIS,HttpModule,OWIN,WebAPI,等等.在这个管道中大致分两个阶 ...
- 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...
随机推荐
- 【百度地图API】如何批量转换为百度经纬度
原文:[百度地图API]如何批量转换为百度经纬度 摘要: 百度地图API的官网上提供了常用坐标转换的示例.但是,一次只能转换一个,真的非常麻烦!!这里结合了官方的示例,自制一个批量转换工具,供大家参考 ...
- hadoop编程小技巧(5)---自己定义输入文件格式类InputFormat
Hadoop代码測试环境:Hadoop2.4 应用:在对数据须要进行一定条件的过滤和简单处理的时候能够使用自己定义输入文件格式类. Hadoop内置的输入文件格式类有: 1)FileInputForm ...
- R.layout.main connot be resolved 和R.java消失
出现例如以下问题: 鼠标放到出代码上面: 分析问题: 1.查看R文件是否被生成.假设没有生成,则勾选build Automatically,然后Clean: 2.假设R文件已生成.则删除去掉代码中: ...
- Team City的安装1
持续集成工具 Team City的安装 前两个月很大一部分精力投入在做部门的持续集成,从概念的了解和工具的选型,再到安装,部署,操作,到最后的真实项目持续集成应用的上线,写了一份手册,包括安装,配置, ...
- ionic入门之基本布局
目录: 简介 Hybrid vs. Others ionic CSS框架 基本布局 布局模式 定高条块:.bar .bar : 位置 .bar : 嵌入子元素 .bar : 嵌入input 内容:.c ...
- 一个大浪Java罢工(一个)安装JDK和环境变量配置
一个.基础知识 (一)什么是Java? Java是一种能够撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台( ...
- 思维方式--SMART原则
假设你的项目管理.系统架构的兴趣,请加微信订阅号"softjg",收藏此PM.建筑师的家 万事开头于你目标的设定,假设開始走错了,那么后面的路将会更加的错误.甚至于更加的努力犯错就 ...
- APP-随身听
简单到复杂听你的专属音响界,听金融.听物业,听新闻和其他节目专辑,简要介绍了新的音频应用,给你不一样的聆听体验.还记得老歌做?这里有.您留声机的一部分!很简单的音频应用,随时随地与此应用程序来听你的私 ...
- C# 通过扩展WebBrowser捕获网络连接错误信息
想捕获WebBrowser连接指定网站过程中发生的错误信息,包括网络无法连接.404找不到网页等等错误!经过网上的搜集,找到了以下解决方案,该解决方案不会在网站连接前发出多余的测试请求. 向Webbr ...
- Spring Resources之介绍和资源接口
1.介绍 不幸的是Java的标准的java.net.URL类和针对不同的URL前缀的标准处理器都不够充分去访问所有的低级资源.例如,美誉标准化的URL实现可能用于去范围需要从classpath中或者相 ...