构成ASP.NET Web API核心框架的消息处理管道既不关心请求消息来源于何处,也不需要考虑响应消息归于何方。当我们采用Web Host模式将一个ASP.NET应用作为目标Web API的宿主时,实际上是由ASP.NET管道解决了这两个问题。具体来说,ASP.NET自身的URL路由系统借助于HttpControllerHandler这个自定义的HttpHandler实现了ASP.NET管道和ASP.NET Web API管道之间的“连通”,但是在Self Host寄宿模式下,请求的监听、接收和响应又是如何实现的呢?[本文已经同步到《How ASP.NET Web API Works?》]

目录
一、HttpBinding模型
    Binding模型
    HttpBinding
    实例演示:直接利用HttpBinding进行请求的接收和响应
二、HttpSelfHostServer
    HttpSelfHostConfiguration
    HttpSelfHostServer与消息处理管道
    实例演示:创建自定义HttpServer模拟HttpSelfHostServer的工作原理

一、HttpBinding模型

和WCF服务一样,我们可以采用Self Host模式将Web API寄宿于任何一种类型的托管应用程序下,宿主可以是一个Windows Form应用、WPF应用、控制台应用以及Windows Service。Self Host模式下的WCF和ASP.NET Web API不仅外在表现形式极为相似,其实在内部实现原理上也是一致的。

Binding模型

对于WCF具有基本了解的读者应该都知道,它是一个基于消息的分布式通信框架,消息交换借助于客户端和服务端对等的终结点(Endpoint)来完成,而终结点由经典的ABC(Address、Binding、Contract)三元素组成。WCF同样具有一个处理消息的管道,这个管道是一组Channel的有序组合,WCF下的Channel相对于ASP.NET Web API下的HttpMessageHandler。

WCF的消息处理管道的缔造者是作为终结点三要素之一的Binding。Binding不仅仅为服务端创建用于接收请求回复响应的管道,同时也为客户端创建发送请求接收响应的管道。Binding模型本身也相对比较复杂,所以我们不可能对其进行详细讨论。如果读者对此比较感兴趣,可以参阅《WCF的绑定模型》。由于ASP.NET Web API只是利用HttpBinding创建服务端消息处理管道,所以我们只讨论Binding的服务端模型。

从结构上讲,一个Binding是若干BindingElement对象的有序组合。对于最终创建的消息处理管道来说,每个Channel都对应着一个BindingElement。BindingElement并非直接创建对应的Channel,由它直接创建的实际上是一个名为ChannelListener的对象,Channel由ChannelListener创建。右图基本揭示了Binding的服务端模型。

顾名思义,ChannelListener用于请求的监听。当Binding对象开启(调用其Open方法)时,每个BindingElement会创建各自的ChannelListener。这些ChannelListener按照对应BindingElement的顺序连接成串,位于底部(面向传输层)的ChannelListener被绑定到某个端口进行请求的监听。一旦探测到抵达的请求,它会利用由所有ChannelListener创建的Channel组成的管道来接收并处理该请求。对于最终需要返回的响应消息,则按照从上到下的顺序被这个管道进行处理并最终返回给客户端。

对于这个由Channel组成消息处理管道来说,有两种类型的Channel是必不可少的。一种是面向传输层用于发送和接收消息的TransportChannel,另一种被称为MessageEncodingChannel则负责对接收的消息实施解码并对发送的消息实施编码。TransportChannel由TransportChannelListener创建,而后者由TransportBindingElement创建。与之类似,MessageEncodingBindingElement是MessageEncodingChannelListener的创建者,而后者又是MessageEncodingChannel的创建者。

如果采用Self Host寄宿模式,请求的监听是由一个类型为HttpBinding的Binding对象创建的ChannelListener管道来完成的,由它创建的管道实现了针对请求的接收和针对响应的回复。HttpBinding类型定义在“System.Web.Http.SelfHost.Channels”命名空间下,我们接下来对它进行详细讲述。

HttpBinding

Binding存在的目的在于创建用于处理和传输消息的信道栈,组成信道栈的每一个Channel均对应着一个BindingElement,所以Binding本身处理消息的能力由其BindingElement的组成来决定,我们可以通过分析BindingElement的组成来了解消息最终是如何处理的。现在我们就来讨论一下ASP.NET Web API在Self Host模式下使用的HttpBinding由哪些BindingElement构成。

如左图所示,HttpBinding仅仅由两种必需的BindingElement构成,TransportBindingElement的类型决定于最终采用的传输协议。如果采用单纯的HTTP协议,采用的TransportBindingElement是一个HttpTransportBindingElement对象。在采用HTTPS协议的情况下,TransportBindingElement的类型是HttpsTransportBindingElement。

我们现在着重来分析与消息编码/解码相关的BindingElement,从图3-11可以看出这是一个HttpMessageEncodingBindingElement对象(HttpMessageEncodingBindingElement是一个定义在程序集“System.Web.Http.SelfHost.dll”中的内部类型),它最终会创建一个MessageEncoder对象完成针对消息的编码/解码工作。

ASP.NET Web API分别利用 HttpRequestMessage和HttpResponseMessage对象表示消息处理管道处理的请求和响应,而WCF消息处理管道的请求和响应均是一个Message对象(Message是定义在命名空间“System.ServiceModel.Channels”下的一个抽象类型)。经过HttpMessageEncoder解码后的Message对象会转成一个HttpRequestMessage对象并传入ASP.NET Web API消息处理管道进行处理,由此管道返回的HttpResponseMessage对象需要转换成一个Message对象并由HttpMessageEncoder根据需求进行解码。

这个具体的消息实际上是一个HttpMessage对象,HttpMessage继承自抽象类Message,它是一个定义在程序集“System.Web.Http.SelfHost.dll”中的内部类型。如下面的代码片断所示,HttpMessage实际上是对一个HttpRequestMessage或者HttpResponseMessage对象的封装,两个方法GetHttpRequestMessage和GetHttpResponseMessage分别用于提取被封装的HttpRequestMessage和HttpResponseMessage对象。

  1. 1: internal sealed class HttpMessage : Message

  1. 2: {

  1. 3: //其他成员

  1. 4: public HttpMessage(HttpRequestMessage request);

  1. 5: public HttpMessage(HttpResponseMessage response);

  1. 6:

  1. 7: public HttpRequestMessage GetHttpRequestMessage(bool extract);

  1. 8: public HttpResponseMessage GetHttpResponseMessage(bool extract);

  1. 9: }

这两个方法均具有一个布尔类型的参数extract,它表示是否“抽取”被封装的HttpRequestMessage/HttpResponseMessage对象。如果指定的参数值为True,方法执行之后被封装的HttpRequestMessage/HttpResponseMessage对象会从HttpMessage对象中抽取出来,所以再次调用它们会返回Null。

再次将我们的关注点拉回到由HttpBinding创建的消息处理管道上。当我们开启HttpBinding后,它利用创建的ChannelListener管道监听请求。一旦探测到抵达的请求后,基于HTTP/HTTPS协议的TransportChannel会负责接收请求。接收的二进制数据会由MessageEncoder解码后生成一个HttpRequestMessage对象,该对象进而被封装成一个HttpMessage对象,传入消息处理管道的HttpRequestMessage是直接通过调用GetHttpRequestMessage方法从该HttpMessage对象中提取的。

当ASP.NET Web API消息处理管道完成了请求的处理并最终输出一个HttpResponseMessage对象后,该对象同样先被封装成一个HttpMessage对象。在通过传输层将响应返回给客户端之前,需要利用MessageEncoder对其进行编码,而解码的内容实际上就是调用GetHttpResponseMessage方法提取的HttpResponseMessage对象。

实例演示:直接利用HttpBinding进行请求的接收和响应

当我们采用Self Host寄宿模式将一个非Web应用程序作为目标Web API的宿主时,最终网络监听任务实际上是由HttpBinding创建的ChannelListener管道来完成的,而ChannelListener管道创建的消息处理管道最终实现了对请求的接收和对响应的发送。为了让读者对此具有深刻的认识,我们通过一个简单的实例来演示如何直接使用HttpBinding实现对请求的监听、接收和响应。

我们创建一个空的控制台程序作为监听服务器,它相当于Self Host寄宿模式下的宿主程序。如下面的代码片断所示,我们创建了一个HttpBinding,并指定监听地址("http://127.0.0.1:3721")调用其BuildChannelListener<IReplyChannel>方法创建了一个ChannelListener管道(返回的是组成管道的第一个ChannelListener对象)。在调用Open方法开启该ChannelListener管道之后,我们调用其AcceptChannel方法创建了消息处理管道,返回的是组成管道的第一个Channel对象。在Open方法将其开启后,我们在一个While循环中调用Channel对象的ReceiveRequest方法进行请求的监听和接收。

  1. 1: class Program

  1. 2: {

  1. 3: static void Main(string[] args)

  1. 4: {

  1. 5: Uri listenUri = new Uri("http://127.0.0.1:3721");

  1. 6: Binding binding = new HttpBinding();

  1. 7: 

  1. 8: //创建、开启信道监听器

  1. 9: IChannelListener<IReplyChannel> channelListener = binding.BuildChannelListener<IReplyChannel>(listenUri);

  1. 10: channelListener.Open();

  1. 11: 

  1. 12: //创建、开启回复信道

  1. 13: IReplyChannel channel = channelListener.AcceptChannel(TimeSpan.MaxValue);

  1. 14: channel.Open();

  1. 15: 

  1. 16: //开始监听

  1. 17: while (true)

  1. 18: {

  1. 19: //接收输出请求消息

  1. 20: RequestContext requestContext = channel.ReceiveRequest(TimeSpan.MaxValue);

  1. 21: PrintRequestMessage(requestContext.RequestMessage);

  1. 22: //消息回复

  1. 23: requestContext.Reply(CreateResponseMessage());

  1. 24: }

  1. 25: }

  1. 26: }

对于成功接收的消息,我们调用具有如下定义的PrintRequestMessage方法将相关的信息打印在控制台上。通过上面的介绍我们知道这个接收到的消息实际上是一个HttpMessage对象,由于这是一个内部类型,所以我们只能以反射的方式调用其GetHttpRequestMessage方法获取被封装的HttpRequestMessage对象。在得到表示请求的HttpRequestMessage对象之后,我们将请求地址和所有报头输出到控制台上。

  1. 1: private static void PrintRequestMessage(Message message)

  1. 2: {

  1. 3: MethodInfo method = message.GetType().GetMethod("GetHttpRequestMessage");

  1. 4: HttpRequestMessage request = (HttpRequestMessage)method.Invoke(message, new object[]{false});

  1. 5: 

  1. 6: Console.WriteLine("{0, -15}:{1}", "RequestUri", request.RequestUri);

  1. 7: foreach (var header in request.Headers)

  1. 8: {

  1. 9: Console.WriteLine("{0, -15}:{1}", header.Key, string.Join("," ,header.Value.ToArray()));

  1. 10: }

  1. 11: }

在对请求进行处理之后,我们需要创建一个Message对象对该请求予以响应,响应消息的创建是通过CreateResponseMessage方法完成的。如下面的代码片断所示,我们首先创建了一个响应状态为“200, OK”的HttpResponseMessage对象,并将其表示主体内容的Content属性设置为一个ObjectContent<Employee>对象。由于我们采用MediaTypeFormatter类型为JsonMediaTypeFormatter,指定的Employee对象会以JSON格式进行序列化。最终的响应消息依然是一个HttpMessage对象,它是对我们创建的HttpResponseMessage对象的封装。限于“内部类型”的限制,我们也不得不采用反射的方式来创建这么一个HttpMessage对象。

  1. 1: private static Message CreateResponseMessage()

  1. 2: {

  1. 3: HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);

  1. 4: Employee employee = new Employee("001","Zhang San","123456","zhangsan@gmail.com");

  1. 5: response.Content = new ObjectContent<Employee>(employee, new JsonMediaTypeFormatter());

  1. 6: 

  1. 7: string httpMessageTypeName = "System.Web.Http.SelfHost.Channels.HttpMessage, System.Web.Http.SelfHost";

  1. 8: Type httpMessageType = Type.GetType(httpMessageTypeName);

  1. 9: return (Message)Activator.CreateInstance(httpMessageType, new object[] { response });

  1. 10: }

  1. 11: 

  1. 12: public class Employee

  1. 13: {

  1. 14: public string Id { get; set; }

  1. 15: public string Name { get; set; }

  1. 16: public string PhoneNo { get; set; }

  1. 17: public string EmailAddress { get; set; }

  1. 18: 

  1. 19: public Employee(string id, string name, string phoneNo, string emailAddress)

  1. 20: {

  1. 21: this.Id = id;

  1. 22: this.Name = name;

  1. 23: this.PhoneNo = phoneNo;

  1. 24: this.EmailAddress = emailAddress;

  1. 25: }

  1. 26: }

当我们直接运行该程序后,实际上就已经启动了针对基地址"http://127.0.0.1:3721"的监听器。现在我们通过浏览器对这个监听器发起请求,为了使请求更像一个针对Web API的调用,我们将请求地址设置为“http://127.0.0.1:3721/employees/001”(看起来好像是获取某个编号为001的员工信息)。如右图所示,通过浏览器发送的请求相关信息会显示在控制台上,而浏览器上也会显示基于JSON格式的员工信息。

二、HttpSelfHostServer

ASP.NET Web API的Self Host寄宿模式是通过一个System.Web.Http.SelfHost.HttpSelfHostServer对象来完成的,那么HttpSelfHostServer与上面介绍的HttpBinding又有何关系呢?HttpSelfHostServer与ASP.NET Web API的消息处理管道又是如何集成的呢?

如下面的代码片断所示,HttpSelfHostServer其实是HttpServer的子类,所以在Self Host模式下HttpSelfHostServer本身就是消息管道的组成部分。换句话说,以HttpSelfHostServer为“龙头”的消息处理管道本身具有请求监听、接收、处理和响应的能力。

  1. 1: public sealed class HttpSelfHostServer : HttpServer

  1. 2: {

  1. 3: public HttpSelfHostServer(HttpSelfHostConfiguration configuration);

  1. 4: public HttpSelfHostServer(HttpSelfHostConfiguration configuration, HttpMessageHandler dispatcher);

  1. 5:

  1. 6: public Task OpenAsync();

  1. 7: public Task CloseAsync();

  1. 8:

  1. 9: protected override void Dispose(bool disposing);

  1. 10: }

ASP.NET Web API的消息处理管道的配置利用通过HttpServer的Configuration属性表示的HttpConfiguration对象来完成,对于HttpSelfHostServer来说,它的Configuration属性返回一个HttpSelfHostConfiguration对象(HttpSelfHostConfiguration类型定义在 “System.Web.Http.SelfHost” 命名空间下)。如上面的代码片断所示,当我们调用构造函数创建一个HttpSelfHostServer对象时,需要通过参数指定此HttpSelfHostConfiguration。

HttpSelfHostConfiguration

如下面的代码片断所示,HttpSelfHostConfiguration直接继承自HttpConfiguration。我们在创建一个HttpSelfHostConfiguration对象的时候需要指定一个Uri对象作为监听基地址,这个地址通过只读属性BaseAddress返回。

  1. 1: public class HttpSelfHostConfiguration : HttpConfiguration

  1. 2: {

  1. 3: public HttpSelfHostConfiguration(Uri baseAddress);

  1. 4:

  1. 5: public Uri BaseAddress { get; }

  1. 6: public HostNameComparisonMode HostNameComparisonMode { get; set; }

  1. 7: public TransferMode TransferMode { get; set; }

  1. 8: 

  1. 9: public int MaxBufferSize { get; set; }

  1. 10: public int MaxConcurrentRequests { get; set; }

  1. 11: public long MaxReceivedMessageSize { get; set; }

  1. 12: 

  1. 13: public TimeSpan ReceiveTimeout { get; set; }

  1. 14: public TimeSpan SendTimeout { get; set; }

  1. 15: 

  1. 16: public HttpClientCredentialType ClientCredentialType { get; set; }

  1. 17: public UserNamePasswordValidator UserNamePasswordValidator { get; set; }

  1. 18: public X509CertificateValidator X509CertificateValidator { get; set; }

  1. 19: }

由于Self Host寄宿模式下请求的监听、接收和响应基本上全部是通过HttpBinding实现的,所以定义在HttpSelfHostConfiguration中的众多属性实际上基本都用于对创建的HttpBinding进行配置。从如下给出的代码片断可以看出HttpBinding类型与HttpSelfHostConfiguration具有类似的属性定义。

  1. 1: public abstract class Binding : IDefaultCommunicationTimeouts

  1. 2: {

  1. 3: //其他成员

  1. 4: public TimeSpan ReceiveTimeout { get; set; }

  1. 5: public TimeSpan SendTimeout { get; set; }

  1. 6: }

  1. 7: 

  1. 8: public class HttpBinding : Binding, IBindingRuntimePreferences

  1. 9: {

  1. 10: //其他成员

  1. 11: public HostNameComparisonMode HostNameComparisonMode { get; set; }

  1. 12: public TransferMode TransferMode { get; set; }

  1. 13: 

  1. 14: public long MaxBufferPoolSize { get; set; }

  1. 15: public int MaxBufferSize { get; set; }

  1. 16: public long MaxReceivedMessageSize { get; set; }

  1. 17: 

  1. 18: public HttpBindingSecurity Security { get; set; }

  1. 19: }

由于Binding在WCF是一个核心组件,其设计本身相对复杂,要深入了解定义在HttpBinding中的这些属性需要相关的背景知识。篇幅所限,我们不能因为这些属性将Binding相关的内容全部搬过来,所以在这里我们仅仅通过下表对它们进行概括性的介绍。

属性

描述

HostNameComparisonMode

如果请求URL没有指定服务器的IP地址而是主机名称,当从URL提取主机名称后会按照相应的比较模式来最终确定匹配的主机名。该属性的类型为System.ServiceModel.HostNameComparisonMode枚举,用以确定主机名比较模式。

TransferMode

MaxBufferSize

消息传输具有Streamed和Buffered两种模式,前者以流的形式进行消息传输,后者则将整个消息的内容先保存于内存缓冲区后一并传输。该属性类型为System.ServiceModel.TransferMode枚举,用以控制针对请求消息和响应消息的传输模式。在默认情况下,请求消息和响应消息均以Buffered模式进行传输。MaxBufferSize属性表示在采用Buffered模式下消息最大缓冲大小,默认值为65536(0x10000)。

MaxConcurrentRequests

MaxReceivedMessageSize

这两个是针对请求的限制,分别表示运行的最大并发访问量和请求消息允许的最大尺寸。两者的默认值分别为100和65536(0x10000)。值得一提的是MaxConcurrentRequests针对最大并发请求的限制是针对单个处理器设定的,对于多处理器或者多核处理来说,应该乘以处理器的数量。

ReceiveTimeout

SendTimeout

这两个属性分别表示接收请求消息和发送响应消息的超时时限,默认值分别为10分钟和1分钟。

ClientCredentialType

UserNamePasswordValidator

X509CertificateValidator

这是三个与安全认证相关的属性。ClientCredentialType表示客户端采用的用户凭证类型,而UserNamePasswordValidator和X509CertificateValidator属性值分别在用户凭证为“用户名+密码”和“X.509证书”的情况下实施认证。

HttpSelfHostServer与消息处理管道

在采用Self Host模式寄宿Web API时,我们会根据指定的监听基地址创建一个HttpSelfHostConfiguration对象,然后据此创建HttpSelfHostServer。当我们调用方法OpenAsync开启它时,HttpSelfHostServer会创建一个HttpBinding对象,并根据指定的HttpSelfHostConfiguration对其作相应的设置。随后HttpBinding会针对指定的监听地址创建一个ChannelListener管道,并调用其BeginOpen方法以异步的方式开启。除了OpenAsync方法外,HttpSelfHostServer还定义了一个CloseAsync方法使我们可以以异步的方式关闭已经开启的ChannelListener管道。

一旦ChannelListener管道被成功开启后,它便绑定到指定的监听地址进行请求的监听。当抵达的请求被探测到,它会利用创建的消息处理管道来接收该请求。通过上面对HttpBinding的介绍我们知道,接收到的二进制数据经过解码之后会生成一个HttpMessage对象,后者是对一个HttpRequestMessage的封装。

接下来HttpSelfHostServer从生成的HttpMessage中提取被封装的HttpRequestMessage对象,并直接分发给后续的HttpMessageHandler作进一步处理。对于最终返回的表示响应的HttpResponseMessage对象,HttpSelfHostServer将其封装成一个HttpMessage对象并利用消息处理管道返回给客户端。在通过传输层发送响应消息之前,HttpMessage会先编码。通过上面的介绍我们知道整个编码工作完全是针对被HttpMessage封装的HttpResponseMessage对象进行的,在HttpResponseMessage中保存的响应内容就是客户端接收到的内容。左图基本揭示了Self Host寄宿模式下整个消息处理管道的结构。

实例演示:创建自定义HttpServer模拟HttpSelfHostServer的工作原理

通过上面的介绍,我想读者朋友们应该对Self Host模式下消息处理管道如何进行请求的监听、接收、处理和响应已经有了全面的了解。如果我们能够创建一个自定义的HttpServer来模拟HttpSelfHostServer的工作原理,我想大家对此的印象一定更加深刻。在本章内容即将完结之前,我们就来完成这么一个演示实例。

我们通过继承HttpServer创建如下一个用于模拟HttpSelfHostServer的MyHttpSelfHostServer类型。两者对于请求的监听、接收和响应的实现原理是一致的,不同之处在于HttpSelfHostServer基本采用异步的操作方式,MyHttpSelfHostServer采用同步编程方式。

  1. 1: public class MyHttpSelfHostServer: HttpServer

  1. 2: {

  1. 3: public Uri BaseAddress { get; private set; }

  1. 4: public IChannelListener<IReplyChannel> ChannelListener { get; private set; }

  1. 5: 

  1. 6: public MyHttpSelfHostServer(HttpConfiguration configuration, Uri baseAddress)

  1. 7: : base(configuration)

  1. 8: {

  1. 9: this.BaseAddress = baseAddress;

  1. 10: }

  1. 11: 

  1. 12: public void Open()

  1. 13: {

  1. 14: HttpBinding binding = new HttpBinding();

  1. 15: this.ChannelListener = binding.BuildChannelListener<IReplyChannel>(this.BaseAddress);

  1. 16: this.ChannelListener.Open();

  1. 17: 

  1. 18: IReplyChannel channnel = this.ChannelListener.AcceptChannel();

  1. 19: channnel.Open();

  1. 20: 

  1. 21: while (true)

  1. 22: {

  1. 23: RequestContext requestContext = channnel.ReceiveRequest(TimeSpan.MaxValue);

  1. 24: Message message = requestContext.RequestMessage;

  1. 25: MethodInfo method = message.GetType().GetMethod("GetHttpRequestMessage");

  1. 26: HttpRequestMessage request = (HttpRequestMessage)method.Invoke(message, new object[] {true});

  1. 27: Task<HttpResponseMessage> processResponse = base.SendAsync(request, new CancellationTokenSource().Token);

  1. 28: processResponse.ContinueWith(task =>

  1. 29: {

  1. 30: string httpMessageTypeName = "System.Web.Http.SelfHost.Channels.HttpMessage, System.Web.Http.SelfHost";

  1. 31: Type httpMessageType = Type.GetType(httpMessageTypeName);

  1. 32: Message reply = (Message)Activator.CreateInstance(httpMessageType, new object[] { task.Result });

  1. 33: requestContext.Reply(reply);

  1. 34: });

  1. 35: }

  1. 36: }

  1. 37: 

  1. 38: public void Close()

  1. 39: {

  1. 40: if (null != this.ChannelListener && this.ChannelListener.State == CommunicationState.Opened)

  1. 41: {

  1. 42: this.ChannelListener.Close();

  1. 43: }

  1. 44: }

  1. 45: }

MyHttpSelfHostServer的只读属性BaseAddress表示监听基地址,该属性直接在构造函数中指定。与HttpSelfHostServer不同的是,用于创建MyHttpSelfHostServer提供的配置对象是一个HttpConfiguration对象而不再是HttpSelfHostConfiguration。

在Open方法中,我们根据提供的监听基地址利用HttpBinding对象创建一个ChannelListener对象,MyHttpSelfHostServer的只读属性ChannelListener引用的也正是这个对象。在开启该ChannelListener之后,我们调用其AccpetChannel方法创建信道栈,最终返回位于栈顶的Channel。在该Channel开启的情况下,我们在一个“永不终止”的While循环中调用其ReceiveRequest方法进行请求的监听。

当信道栈成功接收请求消息后(这是一个HttpMessage对象),我们从中提取出被封装的HttpRequestMessage对象,并将其作为参数调用SendAsync方法,表示请求的HttpReuqestMessage自此进入了消息处理管道这个流水车间。

调用SendAsync方法返回的是一个Task<HttpResponseMessage>对象,我们执行这个Task对象并获得表示响应的HttpResponseMessage对象,然后以反射的形式将其封装成HttpMessage对象。我们最终利用此HttpMessage对象对请求作最终的响应。HttpSelfHostServer定义了OpenAsync和CloseAsync方法开启和关闭监听器,与之相匹配,我们也为Open方法定义了匹配的Close方法来关闭已经开启的ChannelListener。

为了验证我们自定义的MyHttpSelfHostServer是否能够替代“原生”的HttpSelfHostServer,我们在一个控制台中定义了如下一个继承自ApiController的ContactsController。它具有两个重载的Action方法Get,前者用于返回所有的联系人列表,后者返回指定ID的某个联系人信息。

  1. 1: public class ContactsController : ApiController

  1. 2: {

  1. 3: private static List<Contact> contacts = new List<Contact>

  1. 4: {

  1. 5: new Contact{ Id="001", Name = "张三", PhoneNo="123", EmailAddress="zhangsan@gmail.com"},

  1. 6: new Contact{ Id="002",Name = "李四", PhoneNo="456", EmailAddress="lisi@gmail.com"}

  1. 7: };

  1. 8: 

  1. 9: public IEnumerable<Contact> Get()

  1. 10: {

  1. 11: return contacts;

  1. 12: }

  1. 13: 

  1. 14: public Contact Get(string id)

  1. 15: {

  1. 16: return contacts.FirstOrDefault(c => c.Id == id);

  1. 17: }

  1. 18: }

  1. 19: 

  1. 20: public class Contact

  1. 21: {

  1. 22: public string Id { get; set; }

  1. 23: public string Name { get; set; }

  1. 24: public string PhoneNo { get; set; }

  1. 25: public string EmailAddress { get; set; }

  1. 26: }

在作为入口的Main方法中我们编写了如下一段简单的“寄宿”程序。我们根据创建的HttpConfiguration对象和指定的监听基地址(“http://127.0.0.1:3721”)创建了一个MyHttpSelfHostServer对象。在调用Open方法开始监听之前,我们注册了一个URL模板为“http://127.0.0.1:3721”的HttpRoute。

  1. 1: class Program

  1. 2: {

  1. 3: static void Main(string[] args)

  1. 4: {

  1. 5: using (MyHttpSelfHostServer httpServer = new MyHttpSelfHostServer(new HttpConfiguration(), new Uri("http://127.0.0.1:3721")))

  1. 6: {

  1. 7: httpServer.Configuration.Routes.MapHttpRoute(

  1. 8: name : "DefaultApi",

  1. 9: routeTemplate : "api/{controller}/{id}",

  1. 10: defaults : new { id = RouteParameter.Optional });

  1. 11: 

  1. 12: httpServer.Open();

  1. 13: Console.Read();

  1. 14: }

  1. 15: }

  1. 16: }

运行该程序之后,这个“宿主”程序便开始进行请求的监听。现在我们直接利用浏览器对定义在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“不辱使命”。

Self Host模式下的ASP. NET Web API是如何进行请求的监听与处理的?的更多相关文章

  1. ASP.NET Web API是如何根据请求选择Action的?[下篇]

    ASP.NET Web API是如何根据请求选择Action的?[下篇] 再<上篇>中我们简单介绍了用于实现Action选择机制的HttpActionSelector,接下来我们来讨论本章 ...

  2. ASP.NET Web API是如何根据请求选择Action的?[上篇]

    ASP.NET Web API是如何根据请求选择Action的?[上篇] Web API的调用请求总是针对定义在某个HttpController中的某个Action方法,请求响应的内容来源于调用目标A ...

  3. ASP.NET Web API是如何根据请求选择Action的?[上篇] 【转】

    http://www.cnblogs.com/leo_wl/p/3316548.html ASP.NET Web API是如何根据请求选择Action的?[上篇] Web API的调用请求总是针对定义 ...

  4. ASP.NET Web API是如何根据请求选择Action的?[下篇] 【转】

    再<上篇>中我们简单介绍了用于实现Action选择机制的HttpActionSelector,接下来我们来讨论本章最为核心的内 容:ASP.NET Web API如何利用HttpActio ...

  5. 总体介绍ASP.NET Web API下Controller的激活与释放流程

    通过<ASP.NET Web API的Controller是如何被创建的?>我们已经对HttpController激活系统的核心对象有了深刻的了解,这些对象包括用于解析程序集和有效Http ...

  6. ASP.NET Web API下Controller激活

    一.HttpController激活流程 对于组成ASP.NET Web API核心框架的消息处理管道来说,处于末端的HttpMessageHandler是一个HttpRoutingDispatche ...

  7. How ASP.NET Web API 2.0 Works?[持续更新中…]

    一.概述 RESTful Web API [Web标准篇]RESTful Web API [设计篇] 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用 二.路由 ...

  8. 细说Asp.Net Web API消息处理管道(二)

    在细说Asp.Net Web API消息处理管道这篇文章中,通过翻看源码和实例验证的方式,我们知道了Asp.Net Web API消息处理管道的组成类型以及Asp.Net Web API是如何创建消息 ...

  9. ASP.NET Web API 框架研究 Self Host模式下的消息处理管道

    Self Host模式下的ASP.NET Web API与WCF非常相似,都可以寄宿在任意类型的托管应用程序中,宿主可以是Windows Form .WPF.控制台应用以及Windows Servic ...

随机推荐

  1. UWP 应用获取 Localhosts 访问权限

    这是一篇教程,写在 win10 版<量子破碎>发售近期. 主要原因:windows 商城的应用下载实在难以忍受...... so 尝试换一个下载环境(f&q)~ 然而使用 ss 并 ...

  2. 基于Docker快速搭建多节点Hadoop集群--已验证

    Docker最核心的特性之一,就是能够将任何应用包括Hadoop打包到Docker镜像中.这篇教程介绍了利用Docker在单机上快速搭建多节点 Hadoop集群的详细步骤.作者在发现目前的Hadoop ...

  3. View动画和属性动画

    在应用中, 动画效果提升用户体验, 主要分为View动画和属性动画. View动画变换场景图片效果, 效果包括平移(translate), 缩放(scale), 旋转(rotate), 透明(alph ...

  4. log4j:WARN Please initialize the log4j system properly 问题解决

    log4j:WARN No appenders could be found for logger (com.netease.qa.testng.TestngRetry).log4j:WARN Ple ...

  5. LINQ 左右连接

    LINQ 左右连接:DefaultIfEmpty() incomeList = (from p in db.Incomes join m in db.Items on p.ItemID equals ...

  6. 搜索技巧<转>

    平时工作,搜索引擎是少不了的,作为程序员,当然首推 Google.这里简单介绍下几个 Google 搜索的小技巧,方便别人也方便自己查阅. ps:以下所有操作,均可以在 「谷歌搜索首页 -> 设 ...

  7. C++-文件【1】-按行读文本文件的两种方法

    测试环境—— 系统:Win7 64bit 编译器:TDM-GCC 4.9.2 64-bit Release #include <iostream> #include <fstream ...

  8. 利用浏览器LocalStorage缓存图片,视频文件

    文章路径:https://hacks.mozilla.org/2012/02/saving-images-and-files-in-localstorage/

  9. 轻松3步实现c#windowsform窗体美化

    1.需要下载IrisSkin4.dll或者IrisSkin2.dll和ssk皮肤文件. 2.添加引用IrisSkin4.dll或者IrisSkin2.dll到项目中,将下载好的ssk皮肤文件复制到项目 ...

  10. C#编写windows服务

    项目要求: 数据库用有一张表,存放待下载文件的地址,服务需要轮训表将未下载的文件下载下来. 表结构如下: 过程: VS--文件-->新建项目-->windows-->windows服 ...